Skip to content

Commit

Permalink
feat: Upload Data Store file
Browse files Browse the repository at this point in the history
  • Loading branch information
Linko91 committed Sep 19, 2024
1 parent 3c2f471 commit 64f6932
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 42 deletions.
16 changes: 15 additions & 1 deletion frontend/src/api/endpoints/incidentManagement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,11 +232,25 @@ export default {
`/incidents/db_operations/case/data-store/${caseId}`
)
},
downloadCaseDataStoreFileUrl(caseId: number, fileName: string) {
downloadCaseDataStoreFile(caseId: number, fileName: string) {
return HttpClient.get<Blob>(`/incidents/db_operations/case/data-store/download/${caseId}/${fileName}`, {
responseType: "blob"
})
},
uploadCaseDataStoreFile(caseId: number, file: File) {
const form = new FormData()
form.append("file", new Blob([file], { type: file.type }), file.name)

return HttpClient.post<FlaskBaseResponse & { case_data_store: CaseDataStore }>(
`/incidents/db_operations/case/data-store/upload`,
form,
{
params: {
case_id: caseId
}
}
)
},
deleteCaseDataStoreFile(caseId: number, fileName: string) {
return HttpClient.delete<FlaskBaseResponse>(`/incidents/db_operations/case/data-store/${caseId}/${fileName}`)
},
Expand Down
118 changes: 111 additions & 7 deletions frontend/src/components/incidentManagement/cases/CaseDataStore.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,46 @@
<template>
<n-spin :show="loading" class="flex flex-col grow min-h-48" content-class="flex flex-col grow ">
<n-button @click="updateDataStore()">reload</n-button>
<n-spin :show="loading" class="flex flex-col grow min-h-48" content-class="flex flex-col grow gap-4">
<div>
<n-collapse-transition :show="!showUploadForm">
<n-button v-if="dataStore.length" :loading="uploading" type="primary" @click="openUploadForm()">
<template #icon>
<Icon :name="UploadIcon" />
</template>
Upload Data Store file
</n-button>
</n-collapse-transition>

<n-collapse-transition :show="showUploadForm">
<div class="flex flex-col gap-2">
<n-upload v-model:file-list="fileList" :max="1" :disabled="uploading">
<n-upload-dragger>
<div>
<Icon :name="UploadIcon" :size="28" :depth="3"></Icon>
</div>
<div class="font-semibold">Click or drag a file to this area to upload</div>
</n-upload-dragger>
</n-upload>

<div class="flex items-center justify-end gap-3">
<n-button quaternary :disabled="uploading" @click="closeUploadForm()">Close</n-button>

<n-button
:disabled="!isValid"
:loading="uploading"
type="primary"
@click="uploadDataStoreFile()"
>
<template #icon>
<Icon :name="UploadIcon" />
</template>

Upload
</n-button>
</div>
</div>
</n-collapse-transition>
</div>

<div class="flex flex-col gap-2">
<template v-if="dataStore.length">
<CaseDataStoreItem
Expand All @@ -12,7 +52,19 @@
/>
</template>
<template v-else>
<n-empty v-if="!loading" description="No items found" class="justify-center h-48" />
<n-collapse-transition :show="!showUploadForm">
<n-empty v-if="!loading" class="min-h-48">
<div class="flex flex-col items-center gap-4">
<p>No files found</p>
<n-button type="primary" :loading="uploading" @click="openUploadForm()">
<template #icon>
<Icon :name="UploadIcon" />
</template>
Upload a Data Store file
</n-button>
</div>
</n-empty>
</n-collapse-transition>
</template>
</div>
</n-spin>
Expand All @@ -21,9 +73,19 @@
<script setup lang="ts">
import type { CaseDataStore } from "@/types/incidentManagement/cases.d"
import Api from "@/api"
import Icon from "@/components/common/Icon.vue"
import _clone from "lodash/cloneDeep"
import { NButton, NEmpty, NSpin, useMessage } from "naive-ui"
import { onBeforeMount, ref } from "vue"
import {
NButton,
NCollapseTransition,
NEmpty,
NSpin,
NUpload,
NUploadDragger,
type UploadFileInfo,
useMessage
} from "naive-ui"
import { computed, onBeforeMount, ref } from "vue"
import CaseDataStoreItem from "./CaseDataStoreItem.vue"
const { caseId } = defineProps<{
Expand All @@ -35,18 +97,37 @@ const emit = defineEmits<{
(e: "updated"): void
}>()
const UploadIcon = "carbon:cloud-upload"
const message = useMessage()
const loading = ref(false)
const uploading = ref(false)
const showUploadForm = ref(false)
const dataStore = ref<CaseDataStore[]>([])
const fileList = ref<UploadFileInfo[]>([])
const newFile = computed<File | null>(() => fileList.value?.[0]?.file || null)
const isValid = computed(() => {
if (!newFile.value) return false
return true
})
function deleteDataStoreFile(dataStoreFile: CaseDataStore) {
dataStore.value = dataStore.value.filter(o => o.id !== dataStoreFile.id)
emit("deleted")
}
function updateDataStore() {
function updateDataStore(dataStoreFile: CaseDataStore) {
fileList.value = []
dataStore.value.push(dataStoreFile)
emit("updated")
getCaseDataStore(caseId)
}
function openUploadForm() {
showUploadForm.value = true
}
function closeUploadForm() {
showUploadForm.value = false
}
function getCaseDataStore(caseId: number) {
Expand All @@ -69,6 +150,29 @@ function getCaseDataStore(caseId: number) {
})
}
function uploadDataStoreFile() {
if (!newFile.value) return
uploading.value = true
Api.incidentManagement
.uploadCaseDataStoreFile(caseId, newFile.value)
.then(res => {
if (res.data.success) {
message.success(res.data?.message || "Case Data Store File uploaded successfully")
updateDataStore(res.data.case_data_store)
} else {
message.warning(res.data?.message || "An error occurred. Please try again later.")
}
})
.catch(err => {
message.error(err.response?.data?.message || "An error occurred. Please try again later.")
})
.finally(() => {
uploading.value = false
})
}
onBeforeMount(() => {
getCaseDataStore(caseId)
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ import bytes from "bytes"
import { saveAs } from "file-saver"
import _clone from "lodash/cloneDeep"
import _truncate from "lodash/truncate"
import { NButton, NPopconfirm, NSpin, NTooltip, useMessage } from "naive-ui"
import { NButton, NPopconfirm, NSpin, useMessage } from "naive-ui"
import { computed, ref } from "vue"
const { dataStoreFile, embedded } = defineProps<{
Expand All @@ -115,7 +115,7 @@ function downloadFile() {
downloading.value = true
Api.incidentManagement
.downloadCaseDataStoreFileUrl(dataStoreFile.case_id, dataStoreFile.file_name)
.downloadCaseDataStoreFile(dataStoreFile.case_id, dataStoreFile.file_name)
.then(res => {
if (res.data) {
saveAs(res.data, dataStoreFile.file_name)
Expand Down
44 changes: 12 additions & 32 deletions frontend/src/layouts/common/Toolbar/PinnedPages.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
<template>
<div class="flex pinned-pages items-center gap-5">
<TransitionGroup name="anim" tag="div" class="latest-list flex items-center gap-4 overflow-hidden">
<div class="pinned-pages flex items-center gap-5 relative overflow-hidden py-2 justify-end w-full">
<TransitionGroup name="anim" tag="div" class="flex items-center gap-4 overflow-hidden latest-list">
<n-tag
v-for="page of latestSanitized"
:key="page.name"
round
:bordered="false"
:closable="false"
class="bg-transparent flex-shrink overflow-hidden p-0 transition-all duration-300"
@close="removeLatestPage(page.name)"
>
<span class="page-name" :title="page.title" @click="gotoPage(page.name)">
<span
:title="page.title"
class="cursor-pointer ml-0.5 overflow-hidden text-ellipsis hover:underline hover:decoration-2 hover:decoration-[var(--primary-color)]"
@click="gotoPage(page.name)"
>
{{ page.title }}
</span>
<template #icon>
<div class="icon-box" @click="pinPage(page)">
<div
class="cursor-pointer transition-colors duration-300 mr-0.5 hover:text-[var(--primary-color)]"
@click="pinPage(page)"
>
<Icon :size="14" :name="PinnedIcon"></Icon>
</div>
</template>
Expand Down Expand Up @@ -130,12 +138,7 @@ router.afterEach(route => {

<style lang="scss" scoped>
.pinned-pages {
position: relative;
overflow: hidden;
padding: 8px 0;
container-type: inline-size;
justify-content: flex-end;
width: 100%;
:deep() {
.n-tag {
Expand Down Expand Up @@ -179,29 +182,6 @@ router.afterEach(route => {
}
}
.page-name {
cursor: pointer;
margin-left: 2px;
overflow: hidden;
text-overflow: ellipsis;
&:hover {
text-decoration: underline;
text-decoration-thickness: 2px;
text-decoration-color: var(--primary-color);
}
}
.icon-box {
cursor: pointer;
transition: color 0.3s;
margin-right: 2px;
&:hover {
color: var(--primary-color);
}
}
.anim-move,
.anim-enter-active,
.anim-leave-active {
Expand Down

0 comments on commit 64f6932

Please sign in to comment.