Skip to content
Snippets Groups Projects
Commit adecdc5c authored by Valdemar Åstorp Beere's avatar Valdemar Åstorp Beere
Browse files

refactor(component)

add edit avatar in ProfileView
parent 4dc5675c
No related branches found
No related tags found
3 merge requests!66Final merge,!56refactor(component),!4Pipeline fix
public/avatar1.png

96.5 KiB

public/avatar2.png

70.3 KiB

public/avatar3.png

71.4 KiB

public/avatar4.png

108 KiB

public/avatar5.png

82.4 KiB

public/avatar6.png

63.4 KiB

public/avatar7.png

129 KiB

public/avatar8.png

121 KiB

public/avatar9.png

58.5 KiB

...@@ -107,7 +107,6 @@ const editChallenge = (challenge: Challenge) => { ...@@ -107,7 +107,6 @@ const editChallenge = (challenge: Challenge) => {
} }
// Helper methods to get icons // Helper methods to get icons
const getChallengeIcon = (challenge: Challenge): string => { const getChallengeIcon = (challenge: Challenge): string => {
//TODO change to challenge.icon
return `src/assets/coffee.png` return `src/assets/coffee.png`
} }
</script> </script>
<template> <template>
<button @click="openModal" class="text-nowrap">Endre avatar</button> <button @click="openModal" class="primary text-nowrap">Endre avatar</button>
<div <div
v-if="isModalOpen" v-if="isModalOpen"
class="fixed inset-0 bg-black bg-opacity-50 flex justify-center items-center z-50" class="fixed inset-0 bg-black bg-opacity-50 flex justify-center items-center z-50"
> >
<div class="bg-white p-6 rounded-lg shadow-lg max-w-[80vh] h-auto w-full text-center"> <div class="bg-white p-6 rounded-lg shadow-lg max-w-[80vh] h-auto w-full text-center">
<div class="flex flex-row justify-end">
<button @click="closeModal" class="primary ">X</button>
</div>
<h2 class="title">Endre avatar</h2> <h2 class="title">Endre avatar</h2>
<div class="avatar-container flex flex-row justify-between items-center my-8"> <div class="avatar-container flex flex-row justify-between gap-2 items-center my-8">
<button @click="cycleArray('prev')"></button> <button @click="cycleArray('prev')"></button>
<div class="flex flex-row items-center justify-around"> <div class="flex flex-row items-center justify-around">
<img :src="previousAvatar" alt="avatar" class="avatar h-16 w-16" /> <img :src="previousAvatar" alt="avatar" class="avatar h-16 w-16" />
<div class="border-4 rounded-full border-green-600 p-8 mx-4"> <img :src="currentAvatar" alt="avatar" class="avatar block mx-auto h-32 w-32 rounded-full border-green-600 border-2 sm:mx-0 sm:shrink-0" />
<img :src="currentAvatar" alt="avatar" class="avatar h-40 w-40" />
</div>
<img :src="nextAvatar" alt="avatar" class="avatar h-16 w-16" /> <img :src="nextAvatar" alt="avatar" class="avatar h-16 w-16" />
</div> </div>
<button @click="cycleArray('next')"></button> <button @click="cycleArray('next')"></button>
</div> </div>
<button @click="saveAvatar" class="save-button">Lagre</button> <div class="flex flex-row items-center gap-4 mx-auto">
<button @click="saveAvatar" class=" primary save-button basis-1/2">Lagre</button>
<button @click="openFileExplorer" class="primary basis-1/2">Upload New Avatar</button>
</div>
<input type="file" ref="fileInput" @change="handleFileUpload" hidden />
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref, reactive, computed } from 'vue';
import { useUserStore } from "@/stores/userStore";
const isModalOpen = ref(false)
const avatars = [ const userStore = useUserStore();
'src/assets/coffee.png',
'src/assets/head.png', const state = reactive({
'src/assets/nose.png', avatars: [
'src/assets/penger.png', '/avatar1.png',
'src/assets/pig.png' '/avatar2.png',
] '/avatar3.png',
let currentAvatarIndex = 0 '/avatar4.png',
'/avatar5.png',
'/avatar6.png',
'/avatar7.png',
'/avatar8.png',
'/avatar9.png',
],
currentAvatarIndex: 0,
newFile: null, // To hold the new file object
selectedPublicImg: '' // Track blob URLs created for uploaded files
});
const isModalOpen = ref(false);
const fileInput = ref<HTMLElement |null >(null);
const emit = defineEmits(['update-profile-picture']);
const openModal = () => { const openModal = () => {
isModalOpen.value = !isModalOpen.value state.avatars = [
} '/avatar1.png',
'/avatar2.png',
'/avatar3.png',
'/avatar4.png',
'/avatar5.png',
'/avatar6.png',
'/avatar7.png',
'/avatar8.png',
'/avatar9.png',
];
userStore.getProfilePicture();
const urlProfilePicture = userStore.profilePicture;
// Check if a profile picture URL exists and append it to the avatars list
const img = localStorage.getItem('profilePicture') as string;
console.log(state.avatars)
console.log(img)
if (state.avatars.includes(state.selectedPublicImg) || state.avatars.includes(img)) {
// Remove the public asset from the list if it's already selected
state.avatars = state.avatars.filter(avatar => avatar !== state.selectedPublicImg);
console.log(state.avatars, 'state.avatars')
}
// Clear
console.log(state.avatars)
localStorage.removeItem('profilePicture');
state.selectedPublicImg = '';
if (urlProfilePicture) {
state.avatars.push(urlProfilePicture);
state.currentAvatarIndex = state.avatars.length - 1; // Set the current avatar to the profile picture
}
isModalOpen.value = true;
};
const closeModal = () => {
isModalOpen.value = false;
//Remove the uploaded file if there is one.
state.avatars = []
const nextAvatar = ref(avatars[(currentAvatarIndex + 1) % avatars.length]) state.newFile = null; // Clear the new file reference
const currentAvatar = ref(avatars[currentAvatarIndex]) };
const previousAvatar = ref(avatars[(currentAvatarIndex - 1 + avatars.length) % avatars.length])
const cycleArray = (direction: string) => { const cycleArray = (direction: string) => {
if (direction === 'prev') { if (direction === 'prev') {
currentAvatarIndex = (currentAvatarIndex - 1 + avatars.length) % avatars.length state.currentAvatarIndex = (state.currentAvatarIndex - 1 + state.avatars.length) % state.avatars.length;
console.log(currentAvatarIndex) } else {
currentAvatar.value = avatars[currentAvatarIndex] state.currentAvatarIndex = (state.currentAvatarIndex + 1) % state.avatars.length;
previousAvatar.value = avatars[(currentAvatarIndex - 1 + avatars.length) % avatars.length] }
nextAvatar.value = avatars[(currentAvatarIndex + 1) % avatars.length] };
} else {
currentAvatarIndex = (currentAvatarIndex + 1) % avatars.length const handleFileUpload = async (event: any) => {
currentAvatar.value = avatars[currentAvatarIndex] const input = event.target;
previousAvatar.value = avatars[(currentAvatarIndex - 1 + avatars.length) % avatars.length] if (input.files && input.files[0]) {
nextAvatar.value = avatars[(currentAvatarIndex + 1) % avatars.length] const file = input.files[0];
} // Clear any existing temporary blob URLs
} state.avatars = state.avatars.filter(avatar => !avatar.startsWith('blob:'));
state.newFile = file; // Save the new file object for later upload
const saveAvatar = () => { state.avatars.push(URL.createObjectURL(file)); // Add the blob URL for preview
localStorage.setItem('avatar', currentAvatar.value) state.currentAvatarIndex = state.avatars.length - 1; // Set this new avatar as current
isModalOpen.value = false }
} };
const saveAvatar = async () => {
if (state.newFile &&currentAvatar.value.startsWith('blob:')) {
// If there's a new file selected, upload it
const formData = new FormData();
formData.append('file', state.newFile);
await userStore.uploadProfilePicture(formData);
} else if (currentAvatar.value.startsWith('/')) {
// If it's a public asset, fetch it as a blob and upload
state.selectedPublicImg = currentAvatar.value;
const response = await fetch(currentAvatar.value);
const blob = await response.blob();
const file = new File([blob], 'public-avatar.png', { type: blob.type });
const formData = new FormData();
formData.append('file', file);
await userStore.uploadProfilePicture(formData);
localStorage.setItem('profilePicture', currentAvatar.value)
}
closeModal();
emit('update-profile-picture', currentAvatar.value);
};
const openFileExplorer = () => {
fileInput.value?.click();
};
const currentAvatar = computed(() => state.avatars[state.currentAvatarIndex]);
const nextAvatar = computed(() => state.avatars[(state.currentAvatarIndex + 1) % state.avatars.length]);
const previousAvatar = computed(() => state.avatars[(state.currentAvatarIndex - 1 + state.avatars.length) % state.avatars.length]);
</script> </script>
...@@ -21,6 +21,7 @@ export const useUserStore = defineStore('user', () => { ...@@ -21,6 +21,7 @@ export const useUserStore = defineStore('user', () => {
const user = ref<User>(defaultUser) const user = ref<User>(defaultUser)
const errorMessage = ref<string>('') const errorMessage = ref<string>('')
const streak = ref<Streak>() const streak = ref<Streak>()
const profilePicture = ref<string>('')
const register = async ( const register = async (
firstname: string, firstname: string,
...@@ -246,6 +247,27 @@ export const useUserStore = defineStore('user', () => { ...@@ -246,6 +247,27 @@ export const useUserStore = defineStore('user', () => {
user.value.isConfigured = false user.value.isConfigured = false
}) })
} }
// Inside your store or component methods
const uploadProfilePicture = async (formData: FormData) => {
try {
const response = await authInterceptor.post('/profile/picture', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
console.log('Upload successful:', response.data);
} catch (error: any) {
console.error('Failed to upload profile picture:', error.response.data);
}
};
const getProfilePicture = async () => {
try {
const imageResponse = await authInterceptor.get('/profile/picture', { responseType: 'blob' });
profilePicture.value = URL.createObjectURL(imageResponse.data);
} catch (error: any) {
console.error('Failed to retrieve profile picture:', error.response.data);
}
};
return { return {
user, user,
...@@ -257,6 +279,9 @@ export const useUserStore = defineStore('user', () => { ...@@ -257,6 +279,9 @@ export const useUserStore = defineStore('user', () => {
bioRegister, bioRegister,
errorMessage, errorMessage,
getUserStreak, getUserStreak,
streak streak,
uploadProfilePicture,
getProfilePicture,
profilePicture,
} }
}) })
...@@ -15,6 +15,9 @@ const profile = ref<Profile>() ...@@ -15,6 +15,9 @@ const profile = ref<Profile>()
const completedGoals = ref<Goal[]>([]) const completedGoals = ref<Goal[]>([])
const completedChallenges = ref<Challenge[]>([]) const completedChallenges = ref<Challenge[]>([])
const speech = ref<string[]>([]) const speech = ref<string[]>([])
const profilePicture = ref<string>()
const userStore = useUserStore();
const updateUser = async () => { const updateUser = async () => {
authInterceptor('/profile') authInterceptor('/profile')
...@@ -30,6 +33,7 @@ const updateUser = async () => { ...@@ -30,6 +33,7 @@ const updateUser = async () => {
onMounted(async () => { onMounted(async () => {
await updateUser() await updateUser()
await authInterceptor(`/goals/completed?page=0&size=3`) await authInterceptor(`/goals/completed?page=0&size=3`)
.then((response) => { .then((response) => {
completedGoals.value = response.data.content completedGoals.value = response.data.content
...@@ -46,6 +50,8 @@ onMounted(async () => { ...@@ -46,6 +50,8 @@ onMounted(async () => {
return console.log(error) return console.log(error)
}) })
await userStore.getProfilePicture()
profilePicture.value = userStore.profilePicture;
openSpare() openSpare()
}) })
const updateBiometrics = async () => { const updateBiometrics = async () => {
...@@ -53,6 +59,12 @@ const updateBiometrics = async () => { ...@@ -53,6 +59,12 @@ const updateBiometrics = async () => {
await updateUser() await updateUser()
} }
const updateProfilePicture =async () => {
await updateUser()
await userStore.getProfilePicture()
profilePicture.value = userStore.profilePicture;
}
const openSpare = () => { const openSpare = () => {
speech.value = [ speech.value = [
`Velkommen, ${profile.value?.firstName} ${profile.value?.lastName} !`, `Velkommen, ${profile.value?.firstName} ${profile.value?.lastName} !`,
...@@ -68,10 +80,11 @@ const openSpare = () => { ...@@ -68,10 +80,11 @@ const openSpare = () => {
<div class="flex flex-col max-w-96 w-full gap-5"> <div class="flex flex-col max-w-96 w-full gap-5">
<h1>Profile</h1> <h1>Profile</h1>
<div class="flex flex-row gap-5"> <div class="flex flex-row gap-5">
<div class="flex flex-col"> <div class="flex flex-col gap-1">
<div class="w-32 h-32 border-black border-2 rounded-full shrink-0" /> <img :src="profilePicture" alt="could not load" class="block mx-auto h-32 rounded-full border-green-600 border-2 sm:mx-0 sm:shrink-0"/>
<ModalEditAvatar /> <ModalEditAvatar @update-profile-picture="updateProfilePicture" />
</div> </div>
<div class="w-full flex flex-col justify-between"> <div class="w-full flex flex-col justify-between">
<h3 class="font-thin my-0">{{ profile?.username }}</h3> <h3 class="font-thin my-0">{{ profile?.username }}</h3>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment