| 1 | <template> |
| 2 | <div class="soc-notes-list"> |
| 3 | <div class="px-6 pt-3"> |
| 4 | <n-input v-model:value="notesFilter" placeholder="Search notes..." clearable /> |
| 5 | </div> |
| 6 | <n-spin :show="loadingNotes" class="min-h-28"> |
| 7 | <div v-if="notesList?.length" class="flex flex-col gap-2 p-6 pt-3" style="container-type: inline-size"> |
| 8 | <SocCaseNote v-for="note of notesList" :key="note.note_id" :note /> |
| 9 | </div> |
| 10 | <template v-else> |
| 11 | <n-empty v-if="!loadingNotes" description="No items found" class="h-48 justify-center" /> |
| 12 | </template> |
| 13 | </n-spin> |
| 14 | </div> |
| 15 | </template> |
| 16 | |
| 17 | <script setup lang="ts"> |
| 18 | import type { SocNote } from "@/types/soc/note.d" |
| 19 | import { refDebounced } from "@vueuse/core" |
| 20 | import axios from "axios" |
| 21 | import { NEmpty, NInput, NSpin, useMessage } from "naive-ui" |
| 22 | import { onBeforeMount, ref, toRefs, watch } from "vue" |
| 23 | import Api from "@/api" |
| 24 | import SocCaseNote from "./SocCaseNote.vue" |
| 25 | |
| 26 | const props = defineProps<{ caseId: string | number }>() |
| 27 | |
| 28 | const requested = defineModel<boolean | undefined>("requested", { default: false }) |
| 29 | |
| 30 | const { caseId } = toRefs(props) |
| 31 | |
| 32 | const loadingNotes = ref(false) |
| 33 | const message = useMessage() |
| 34 | const notesFilter = ref("") |
| 35 | const notesFilterDebounced = refDebounced(notesFilter, 1000) |
| 36 | let abortControllerNotes: AbortController | null = null |
| 37 | |
| 38 | const notesList = ref<SocNote[] | null>(null) |
| 39 | |
| 40 | function getNotes() { |
| 41 | loadingNotes.value = true |
| 42 | |
| 43 | abortControllerNotes = new AbortController() |
| 44 | |
| 45 | Api.soc |
| 46 | .getNotesByCase( |
| 47 | caseId.value.toString(), |
| 48 | { searchTerm: notesFilterDebounced.value || "" }, |
| 49 | abortControllerNotes.signal |
| 50 | ) |
| 51 | .then(res => { |
| 52 | if (res.data.success) { |
| 53 | notesList.value = res.data?.notes || null |
| 54 | } else { |
| 55 | message.warning(res.data?.message || "An error occurred. Please try again later.") |
| 56 | } |
| 57 | }) |
| 58 | .catch(err => { |
| 59 | if (!axios.isCancel(err)) { |
| 60 | message.error(err.response?.data?.message || "An error occurred. Please try again later.") |
| 61 | } |
| 62 | }) |
| 63 | .finally(() => { |
| 64 | loadingNotes.value = false |
| 65 | }) |
| 66 | } |
| 67 | |
| 68 | watch(notesFilterDebounced, () => { |
| 69 | if (abortControllerNotes !== null) { |
| 70 | abortControllerNotes?.abort() |
| 71 | } |
| 72 | |
| 73 | setTimeout(() => { |
| 74 | getNotes() |
| 75 | }, 300) |
| 76 | }) |
| 77 | |
| 78 | watch(requested, val => { |
| 79 | if (val) { |
| 80 | getNotes() |
| 81 | } |
| 82 | |
| 83 | requested.value = false |
| 84 | }) |
| 85 | |
| 86 | onBeforeMount(() => { |
| 87 | getNotes() |
| 88 | abortControllerNotes?.abort() |
| 89 | }) |
| 90 | </script> |