feat: file upload composable

This commit is contained in:
Reto Aebersold 2023-11-09 10:01:36 +01:00 committed by Christian Cueni
parent 7ccdcf51f2
commit 4791a776d4
4 changed files with 76 additions and 52 deletions

View File

@ -6,6 +6,7 @@ import {
circleFlatLearningContents,
circleFlatLearningUnits,
} from "@/services/circle";
import { presignUpload, uploadFile } from "@/services/files";
import { useCompletionStore } from "@/stores/completion";
import { useCourseSessionsStore } from "@/stores/courseSessions";
import { useDashboardStore } from "@/stores/dashboard";
@ -435,3 +436,30 @@ export function useCourseStatistics() {
return { courseSessionName, circleMeta };
}
export function useFileUpload() {
const error = ref(false);
const loading = ref(false);
const fileInfo = ref({} as { id: string; name: string; url: string });
async function upload(e: Event) {
const { files } = e.target as HTMLInputElement;
if (!files?.length) return;
try {
error.value = false;
loading.value = true;
const file = files[0];
const presignData = await presignUpload(file);
await uploadFile(presignData.pre_sign, file);
fileInfo.value = presignData.file_info;
} catch (e) {
console.error(e);
error.value = true;
} finally {
loading.value = false;
}
}
return { upload, error, loading, fileInfo };
}

View File

@ -1,7 +1,7 @@
<script setup lang="ts">
import { ref, watch } from "vue";
import { presignUpload, uploadFile } from "@/services/files";
import type { UserDataFileInfo } from "@/types";
import { useFileUpload } from "@/composables";
const props = defineProps<{
fileInfo: UserDataFileInfo | null;
@ -10,6 +10,12 @@ const props = defineProps<{
const emit = defineEmits(["fileUploaded", "fileDeleted"]);
const selectedFile = ref();
const {
upload: uploadFile,
loading: uploadLoading,
error: uploadError,
fileInfo: uploadInfo,
} = useFileUpload();
watch(
() => props.fileInfo,
@ -19,28 +25,11 @@ watch(
{ immediate: true }
);
const loading = ref(false);
const uploadError = ref(false);
async function fileSelected(e: Event) {
const { files } = e.target as HTMLInputElement;
if (!files?.length) return;
try {
uploadError.value = false;
loading.value = true;
const file = files[0];
const presignData = await presignUpload(file);
await uploadFile(presignData.pre_sign, file);
selectedFile.value = presignData.file_info;
emit("fileUploaded", presignData.file_info.id);
} catch (error) {
console.error(error);
uploadError.value = true;
} finally {
loading.value = false;
}
}
watch(uploadInfo, (info) => {
console.log("fileInfo changed", info);
selectedFile.value = info;
emit("fileUploaded", info.id);
});
function handleDelete() {
selectedFile.value = null;
@ -52,7 +41,7 @@ function handleDelete() {
<div>
<h4 class="mb-2 text-xl">{{ $t("a.Datei hochladen") }}</h4>
<template v-if="loading">
<template v-if="uploadLoading">
{{ $t("a.Laden...") }}
</template>
@ -68,7 +57,8 @@ function handleDelete() {
type="file"
class="absolute opacity-0"
accept=".pdf,.jpg,.jpeg,.png,.doc,.docx,.mov,.ppt,.pptx,.mp4"
@change="fileSelected"
:disabled="uploadLoading"
@change="uploadFile"
/>
<it-icon-document class="mr-1.5 h-7 w-7 text-blue-800" />
{{ $t("a.Datei auswählen") }}

View File

@ -1,10 +1,10 @@
<script setup lang="ts">
import WizardPage from "@/components/onboarding/WizardPage.vue";
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
import { ref } from "vue";
import { ref, watch } from "vue";
import { useUserStore } from "@/stores/user";
import AvatarImage from "@/components/ui/AvatarImage.vue";
import { presignUpload, uploadFile } from "@/services/files";
import { useFileUpload } from "@/composables";
const user = useUserStore();
@ -14,29 +14,22 @@ const companies = [
{ id: 3, name: "Firma 3" },
];
const uploadError = ref(false);
const avatarLoading = ref(false);
async function fileSelected(e: Event) {
const { files } = e.target as HTMLInputElement;
if (!files?.length) return;
try {
uploadError.value = false;
avatarLoading.value = true;
const file = files[0];
const presignData = await presignUpload(file);
await uploadFile(presignData.pre_sign, file);
user.avatar_url = presignData.file_info.url;
} catch (error) {
console.error(error);
uploadError.value = true;
} finally {
avatarLoading.value = false;
}
}
const selectedCompany = ref(companies[0]);
const {
upload: avatarUpload,
loading: avatarLoading,
error: avatarError,
fileInfo: avatarFileInfo,
} = useFileUpload();
watch(avatarFileInfo, (info) => {
console.log("fileInfo changed", info);
});
watch(selectedCompany, (company) => {
console.log("company changed", company);
});
</script>
<template>
@ -66,12 +59,13 @@ const selectedCompany = ref(companies[0]);
type="file"
class="absolute opacity-0"
accept="image/*"
@change="fileSelected"
:disabled="avatarLoading"
@change="avatarUpload"
/>
{{ $t("a.Bild hochladen") }}
<it-icon-upload class="it-icon ml-2 h-6 w-6" />
</div>
<p v-if="uploadError" class="mt-3 text-red-700">
<p v-if="avatarError" class="mt-3 text-red-700">
{{ $t("a.Datei kann nicht gespeichert werden.") }}
</p>
</div>

View File

@ -107,7 +107,19 @@ export async function fetchCourseSessionDocuments(courseSessionId: string) {
return itGetCached(`/api/core/document/list/${courseSessionId}/`);
}
export async function presignUpload(file: File) {
type PresignResponse = {
pre_sign: {
url: string;
fields: Record<string, string>;
};
file_info: {
id: string;
name: string;
url: string;
};
};
export async function presignUpload(file: File): Promise<PresignResponse> {
return await itPost(`/api/core/storage/presign/`, {
file_type: file.type,
file_name: file.name,