feat: avatar upload
This commit is contained in:
parent
2d2b5a86c3
commit
a40f066279
|
|
@ -1,10 +1,34 @@
|
|||
<script setup lang="ts">
|
||||
import { useEntities } from "@/services/entities";
|
||||
import AvatarImage from "@/components/ui/AvatarImage.vue";
|
||||
import { ref } from "vue";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
|
||||
const { countries, organisations } = useEntities();
|
||||
|
||||
const props = defineProps(["modelValue"]);
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
const emit = defineEmits(["update:modelValue", "inlineEditComplete"]);
|
||||
|
||||
const user = useUserStore();
|
||||
|
||||
const avatarLoading = ref(false);
|
||||
const avatarError = ref(false);
|
||||
|
||||
async function avatarUpload(e: Event) {
|
||||
const { files } = e.target as HTMLInputElement;
|
||||
if (!files?.length) return;
|
||||
avatarLoading.value = true;
|
||||
avatarError.value = false;
|
||||
|
||||
try {
|
||||
await user.setUserAvatar(files[0]);
|
||||
} catch (e) {
|
||||
avatarError.value = true;
|
||||
} finally {
|
||||
avatarLoading.value = false;
|
||||
emit("inlineEditComplete");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -70,6 +94,28 @@ const emit = defineEmits(["update:modelValue"]);
|
|||
})
|
||||
"
|
||||
/>
|
||||
<label class="block pb-1.5 leading-6">
|
||||
{{ $t("a.Profilbild") }}
|
||||
</label>
|
||||
<div class="flex items-center space-x-4">
|
||||
<AvatarImage
|
||||
:image-size="64"
|
||||
:loading="avatarLoading"
|
||||
:image-url="user.avatar_url"
|
||||
/>
|
||||
<div class="btn-secondary relative inline-flex cursor-pointer items-center">
|
||||
<input
|
||||
id="upload"
|
||||
type="file"
|
||||
class="absolute bottom-0 left-0 right-0 top-0 opacity-0"
|
||||
accept="image/*"
|
||||
:disabled="avatarLoading"
|
||||
@change="avatarUpload"
|
||||
/>
|
||||
{{ $t("a.Bild hochladen") }}
|
||||
<it-icon-upload class="it-icon ml-2 h-6 w-6" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="mt-8 border-t pt-8">
|
||||
<h4 class="mb-4 text-lg font-bold">
|
||||
|
|
|
|||
|
|
@ -1,23 +1,34 @@
|
|||
<script setup lang="ts">
|
||||
import LoadingSpinner from "@/components/ui/LoadingSpinner.vue";
|
||||
import { computed } from "vue";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
imageUrl?: string;
|
||||
loading?: boolean;
|
||||
imageSize?: number;
|
||||
}>(),
|
||||
{
|
||||
imageUrl: "",
|
||||
loading: false,
|
||||
imageSize: 172,
|
||||
}
|
||||
);
|
||||
|
||||
const imageStyle = computed(() => {
|
||||
return {
|
||||
width: `${props.imageSize}px`,
|
||||
height: `${props.imageSize}px`,
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative">
|
||||
<img
|
||||
v-if="props?.imageUrl"
|
||||
class="aspect-square w-[172px] rounded-full object-cover"
|
||||
class="aspect-square rounded-full object-cover"
|
||||
:style="imageStyle"
|
||||
:class="{ 'opacity-30': props.loading }"
|
||||
:src="props.imageUrl"
|
||||
alt="avatar"
|
||||
|
|
@ -26,8 +37,8 @@ const props = withDefaults(
|
|||
v-else
|
||||
:class="{ 'opacity-30': props.loading }"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="172"
|
||||
height="172"
|
||||
:width="imageSize"
|
||||
:height="imageSize"
|
||||
viewBox="0 0 172 172"
|
||||
fill="none"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -36,6 +36,14 @@ function startEditMode() {
|
|||
editMode.value = true;
|
||||
}
|
||||
|
||||
function exitEditMode() {
|
||||
editMode.value = false;
|
||||
saved.value = true;
|
||||
setTimeout(() => {
|
||||
saved.value = false;
|
||||
}, 10 * 1000);
|
||||
}
|
||||
|
||||
async function save() {
|
||||
const profileData = Object.assign({}, formData.value);
|
||||
profileData.country = countries.value.find(
|
||||
|
|
@ -46,11 +54,7 @@ async function save() {
|
|||
);
|
||||
profileData.organisation = parseInt(profileData.organisation);
|
||||
await user.updateUserProfile(profileData);
|
||||
editMode.value = false;
|
||||
saved.value = true;
|
||||
setTimeout(() => {
|
||||
saved.value = false;
|
||||
}, 10 * 1000);
|
||||
exitEditMode();
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
@ -89,7 +93,7 @@ async function save() {
|
|||
<span>{{ $t("a.Deine Änderungen wurden gespeichert") }}.</span>
|
||||
</div>
|
||||
<template v-if="editMode">
|
||||
<ProfileEdit v-model="formData" />
|
||||
<ProfileEdit v-model="formData" @inline-edit-complete="exitEditMode" />
|
||||
<div class="flex justify-end space-x-4">
|
||||
<button class="btn btn-secondary" @click="editMode = false">
|
||||
{{ $t("general.cancel") }}
|
||||
|
|
|
|||
Loading…
Reference in New Issue