App.vue 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. <template>
  2. <div class="card-container">
  3. <ConfirmDialog></ConfirmDialog>
  4. <ConfirmDialog group="editing">
  5. <template #message="prop">
  6. <div class="flex flex-column">
  7. <label for="edit-title">Title</label>
  8. <InputText id="edit-title" v-model="prop.message.item[3].title"/>
  9. <label for="edit-source">Title</label>
  10. <InputText id="edit-source" v-model="prop.message.item[3].source"/>
  11. <label>Paragraph</label>
  12. <Textarea v-model="prop.message.item[2]" auto-resize rows="5" style="width: 100%"/>
  13. <label>ID: <i>{{ prop.message.item[0] }}</i></label>
  14. </div>
  15. </template>
  16. </ConfirmDialog>
  17. <form @submit="onSubmit">
  18. <Card>
  19. <template #title>IPT Question Search</template>
  20. <template #content>
  21. <span class="p-float-label">
  22. <Textarea v-model="text_area" auto-resize rows="10" style="width: 100%"/>
  23. <label>Paragraph</label>
  24. </span>
  25. </template>
  26. <template #footer>
  27. <div class="flex flex-row">
  28. <Button :loading="search_loading" class="flex align-items-center justify-content-center m-2" icon="pi pi-search" type="submit" label="Search" />
  29. <div class="p-inputgroup flex align-items-center justify-content-center m-2">
  30. <span class="p-float-label">
  31. <InputText id="source_field" v-model="source" />
  32. <label for="source_field">Source</label>
  33. </span>
  34. <Button :disabled="source.length === 0" icon="pi pi-plus" severity="secondary" label="Add" @click="onAdd"/>
  35. </div>
  36. </div>
  37. </template>
  38. </Card>
  39. </form>
  40. <Card>
  41. <template #title>Results</template>
  42. <template #content>
  43. <DataView :value="results">
  44. <template #list="item">
  45. <div class="col-12">
  46. <div class="flex flex-column">
  47. <div class="flex justify-content-between flex-wrap">
  48. <div class="flex align-items-center justify-content-center">
  49. <Tag :severity="tag_colour(item.data[1])" :value="item.data[1].toFixed(4)" class="mr-2"></Tag>
  50. <span>
  51. <b>{{ item.data[3].title }}</b>
  52. {{ item.data[3].source }}
  53. </span>
  54. </div>
  55. <div class="flex align-items-center justify-content-center">
  56. <Button icon="pi pi-file-edit" severity="primary" @click="edit_item(item.data)" text rounded></Button>
  57. <Button icon="pi pi-trash" severity="danger" @click="confirm_delete(item.data[0])" text rounded></Button>
  58. </div>
  59. </div>
  60. <p class="flex align-items-center"> {{ item.data[2] }}</p>
  61. </div>
  62. </div>
  63. </template>
  64. </DataView>
  65. </template>
  66. </Card>
  67. <ProgressSpinner v-show="uploading_db"/>
  68. <Accordion :activeIndex="-1" style="width: 100%" class="m-3" v-show="!uploading_db">
  69. <AccordionTab header="Options">
  70. <div class="flex flex-row">
  71. <Button label="Download database" icon="pi pi-download" class="mr-2" @click="download_db"/>
  72. <FileUpload mode="basic" name="database" url="/api/v1/database" accept="text/csv"
  73. chooseLabel="Upload database" @before-upload="uploading_db = true" @upload="uploading_db = false"/>
  74. </div>
  75. </AccordionTab>
  76. </Accordion>
  77. </div>
  78. </template>
  79. <script setup>
  80. import Card from "primevue/card"
  81. import Tag from "primevue/tag"
  82. import Button from "primevue/button"
  83. import Textarea from "primevue/textarea"
  84. import InputText from "primevue/inputtext"
  85. import DataView from 'primevue/dataview';
  86. import ConfirmDialog from 'primevue/confirmdialog';
  87. import { useConfirm } from "primevue/useconfirm";
  88. import AccordionTab from 'primevue/accordiontab';
  89. import Accordion from 'primevue/accordion';
  90. import FileUpload from 'primevue/fileupload';
  91. import ProgressSpinner from 'primevue/progressspinner';
  92. import { ref, reactive } from "vue";
  93. const text_area = ref("");
  94. const source = ref("");
  95. const search_loading = ref(false);
  96. const results = reactive([]);
  97. const confirm = useConfirm();
  98. const uploading_db = ref(false);
  99. const confirm_delete = (item_id) => {
  100. confirm.require({
  101. message: 'Do you want to delete this record?',
  102. header: 'Delete Confirmation',
  103. icon: 'pi pi-info-circle',
  104. acceptClass: 'p-button-danger',
  105. accept: async () => {
  106. await fetch(`/api/v1/item?id=${item_id}`, { method: "DELETE"} )
  107. },
  108. });
  109. };
  110. const tag_colour = (dist) => {
  111. if (dist < 0.30) return 'success';
  112. if (dist < 0.35) return 'warning';
  113. return 'danger';
  114. }
  115. const download_db = () => {
  116. window.open('/api/v1/database');
  117. }
  118. const edit_item = (item) => {
  119. confirm.require({
  120. group: 'editing',
  121. item: item,
  122. header: `Editing "${item[3].title}"`,
  123. acceptClass: 'p-button-primal',
  124. acceptLabel: 'Edit',
  125. rejectLabel: 'Cancel',
  126. accept: async () => {
  127. await fetch(`/api/v1/item?id=${item[0]}`, { method: "PUT", headers: {
  128. "Content-Type": "application/json",
  129. }, body: JSON.stringify({document: item[2], metadata: item[3] })} )
  130. },
  131. });
  132. };
  133. async function onSubmit(event) {
  134. event.preventDefault()
  135. event.stopPropagation()
  136. search_loading.value = true
  137. results.length = 0
  138. const response = await fetch('/api/v1/search', {
  139. method: "POST",
  140. headers: {
  141. "Content-Type": "application/json",
  142. }, body: JSON.stringify({text: text_area.value })
  143. })
  144. let data = await response.json()
  145. data.forEach(a => results.push(a))
  146. search_loading.value = false
  147. }
  148. async function onAdd() {
  149. await fetch('/api/v1/item', {
  150. method: "POST",
  151. headers: {
  152. "Content-Type": "application/json",
  153. }, body: JSON.stringify({body: text_area.value, source: source.value})
  154. })
  155. text_area.value = ''
  156. }
  157. </script>
  158. <style>
  159. .card-container {
  160. display: flex;
  161. flex-flow: row wrap;
  162. justify-content: center;
  163. padding-bottom: 5em;
  164. }
  165. .card-container .p-card {
  166. width: calc(50vw - 2em);
  167. min-width: calc(min(600px, 100vw));
  168. margin: 0.5em 0.5em 0.5em 0.5em;
  169. }
  170. </style>