AddDocModal.svelte 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. <script lang="ts">
  2. import { toast } from 'svelte-sonner';
  3. import dayjs from 'dayjs';
  4. import { onMount } from 'svelte';
  5. import { createNewDoc, getDocs, tagDocByName, updateDocByName } from '$lib/apis/documents';
  6. import Modal from '../common/Modal.svelte';
  7. import { documents } from '$lib/stores';
  8. import TagInput from '../common/Tags/TagInput.svelte';
  9. import Tags from '../common/Tags.svelte';
  10. import { addTagById } from '$lib/apis/chats';
  11. import { uploadDocToVectorDB } from '$lib/apis/rag';
  12. import { transformFileName } from '$lib/utils';
  13. import { SUPPORTED_FILE_EXTENSIONS, SUPPORTED_FILE_TYPE } from '$lib/constants';
  14. export let show = false;
  15. export let selectedDoc;
  16. let inputFiles;
  17. let tags = [];
  18. let doc = {
  19. name: '',
  20. title: '',
  21. content: null
  22. };
  23. const uploadDoc = async (file) => {
  24. const res = await uploadDocToVectorDB(localStorage.token, '', file).catch((error) => {
  25. toast.error(error);
  26. return null;
  27. });
  28. if (res) {
  29. await createNewDoc(
  30. localStorage.token,
  31. res.collection_name,
  32. res.filename,
  33. transformFileName(res.filename),
  34. res.filename,
  35. tags.length > 0
  36. ? {
  37. tags: tags
  38. }
  39. : null
  40. ).catch((error) => {
  41. toast.error(error);
  42. return null;
  43. });
  44. await documents.set(await getDocs(localStorage.token));
  45. }
  46. };
  47. const submitHandler = async () => {
  48. if (inputFiles && inputFiles.length > 0) {
  49. for (const file of inputFiles) {
  50. console.log(file, file.name.split('.').at(-1));
  51. if (
  52. SUPPORTED_FILE_TYPE.includes(file['type']) ||
  53. SUPPORTED_FILE_EXTENSIONS.includes(file.name.split('.').at(-1))
  54. ) {
  55. uploadDoc(file);
  56. } else {
  57. toast.error(
  58. `Unknown File Type '${file['type']}', but accepting and treating as plain text`
  59. );
  60. uploadDoc(file);
  61. }
  62. }
  63. inputFiles = null;
  64. document.getElementById('upload-doc-input').value = '';
  65. } else {
  66. toast.error(`File not found.`);
  67. }
  68. show = false;
  69. documents.set(await getDocs(localStorage.token));
  70. };
  71. const addTagHandler = async (tagName) => {
  72. if (!tags.find((tag) => tag.name === tagName) && tagName !== '') {
  73. tags = [...tags, { name: tagName }];
  74. } else {
  75. console.log('tag already exists');
  76. }
  77. };
  78. const deleteTagHandler = async (tagName) => {
  79. tags = tags.filter((tag) => tag.name !== tagName);
  80. };
  81. onMount(() => {});
  82. </script>
  83. <Modal size="sm" bind:show>
  84. <div>
  85. <div class=" flex justify-between dark:text-gray-300 px-5 py-4">
  86. <div class=" text-lg font-medium self-center">Add Docs</div>
  87. <button
  88. class="self-center"
  89. on:click={() => {
  90. show = false;
  91. }}
  92. >
  93. <svg
  94. xmlns="http://www.w3.org/2000/svg"
  95. viewBox="0 0 20 20"
  96. fill="currentColor"
  97. class="w-5 h-5"
  98. >
  99. <path
  100. d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
  101. />
  102. </svg>
  103. </button>
  104. </div>
  105. <hr class=" dark:border-gray-800" />
  106. <div class="flex flex-col md:flex-row w-full px-5 py-4 md:space-x-4 dark:text-gray-200">
  107. <div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
  108. <form
  109. class="flex flex-col w-full"
  110. on:submit|preventDefault={() => {
  111. submitHandler();
  112. }}
  113. >
  114. <div class="mb-3 w-full">
  115. <input id="upload-doc-input" hidden bind:files={inputFiles} type="file" multiple />
  116. <button
  117. class="w-full text-sm font-medium py-3 bg-gray-850 hover:bg-gray-800 text-center rounded-xl"
  118. type="button"
  119. on:click={() => {
  120. document.getElementById('upload-doc-input')?.click();
  121. }}
  122. >
  123. {#if inputFiles}
  124. {inputFiles.length > 0 ? `${inputFiles.length}` : ''} document(s) selected.
  125. {:else}
  126. Click here to select documents.
  127. {/if}
  128. </button>
  129. </div>
  130. <div class=" flex flex-col space-y-1.5">
  131. <div class="flex flex-col w-full">
  132. <div class=" mb-1.5 text-xs text-gray-500">Tags</div>
  133. <Tags {tags} addTag={addTagHandler} deleteTag={deleteTagHandler} />
  134. </div>
  135. </div>
  136. <div class="flex justify-end pt-5 text-sm font-medium">
  137. <button
  138. class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
  139. type="submit"
  140. >
  141. Save
  142. </button>
  143. </div>
  144. </form>
  145. </div>
  146. </div>
  147. </div>
  148. </Modal>
  149. <style>
  150. input::-webkit-outer-spin-button,
  151. input::-webkit-inner-spin-button {
  152. /* display: none; <- Crashes Chrome on hover */
  153. -webkit-appearance: none;
  154. margin: 0; /* <-- Apparently some margin are still there even though it's hidden */
  155. }
  156. .tabs::-webkit-scrollbar {
  157. display: none; /* for Chrome, Safari and Opera */
  158. }
  159. .tabs {
  160. -ms-overflow-style: none; /* IE and Edge */
  161. scrollbar-width: none; /* Firefox */
  162. }
  163. input[type='number'] {
  164. -moz-appearance: textfield; /* Firefox */
  165. }
  166. </style>