diff --git a/src/util/API.js b/src/util/API.js index d6134cfb94f4b8d764395e8347ac37f74e023c47..51d320f715401697aceca2dd07dda75c07cee7e2 100644 --- a/src/util/API.js +++ b/src/util/API.js @@ -5,134 +5,158 @@ import router from "@/router/index"; export const API = { - /** - * API method to send a login request. - * If login succeeds, the logged in User and their token - * is saved to the Pinia AuthStore - * - * @param email email address of the user to log in as - * @param password password to log in with - * @returns a Result with whether the login attempt succeeded - */ - login: async (request) => { - const authStore = useAuthStore(); - let token; - authStore.logout(); //in case someone for some reason is logged in - - return axios.post( - `${import.meta.env.VITE_BACKEND_URL}/login`, - request, - ) - .then(async (response) => { - token = response.data; - const id = (jwt_decode(token)).id; - - return API.getAccount(id, token) - .then((user) => { - authStore.setAccount(user); - authStore.setToken(token); - API.getProfiles() - .then(response => {authStore.setProfiles(response)}) - .catch(() => {throw new Error()}) - return; - }) - .catch(() => { - throw new Error(); - }); - }) - .catch(() => { - throw new Error(); - }); - }, - - - /** - * API method to get a account by their ID - * @param id ID number of the account to retrieve - * @returns A promise that resolves to a User if the API call succeeds, - * or is rejected if the API call fails - */ - getAccount: async (id, token) => { - return axios.get(`${import.meta.env.VITE_BACKEND_URL}/account/${id}`, { - headers: { Authorization: `Bearer ${token}` }, - }) - .then((response) => { - return response.data; + /** + * API method to send a login request. + * If login succeeds, the logged in User and their token + * is saved to the Pinia AuthStore + * + * @param email email address of the user to log in as + * @param password password to log in with + * @returns a Result with whether the login attempt succeeded + */ + login: async (request) => { + const authStore = useAuthStore(); + let token; + authStore.logout(); //in case someone for some reason is logged in + + return axios.post( + `${import.meta.env.VITE_BACKEND_URL}/login`, + request, + ) + .then(async (response) => { + token = response.data; + const id = (jwt_decode(token)).id; + + return API.getAccount(id, token) + .then((user) => { + authStore.setAccount(user); + authStore.setToken(token); + API.getProfiles() + .then(response => { authStore.setProfiles(response) }) + .catch(() => { throw new Error() }) + return; }) .catch(() => { - throw new Error("Account not found or not accessible"); + throw new Error(); }); - }, + }) + .catch(() => { + throw new Error(); + }); + }, - // Sends the user into the home page logged in as the profile they clicked on - selectProfile: async (id) => { - const authStore = useAuthStore() - return axios.get(`${import.meta.env.VITE_BACKEND_URL}/profile/${id}`, { - headers: { Authorization: `Bearer ${authStore.token}` }, - }) - .then((response) => { - authStore.setProfile(response.data) - router.push("/") - - }) - .catch(() => { - throw new Error("Profile not found or not accessible") - }) + /** + * API method to get a account by their ID + * @param id ID number of the account to retrieve + * @returns A promise that resolves to a User if the API call succeeds, + * or is rejected if the API call fails + */ + getAccount: async (id, token) => { + return axios.get(`${import.meta.env.VITE_BACKEND_URL}/account/${id}`, { + headers: { Authorization: `Bearer ${token}` }, + }) + .then((response) => { + return response.data; + }) + .catch(() => { + throw new Error("Account not found or not accessible"); + }); + }, - }, + // Sends the user into the home page logged in as the profile they clicked on + selectProfile: async (id) => { + const authStore = useAuthStore() + return axios.get(`${import.meta.env.VITE_BACKEND_URL}/profile/${id}`, { + headers: { Authorization: `Bearer ${authStore.token}` }, + }) + .then((response) => { + authStore.setProfile(response.data) + router.push("/") - /** - * Sends a request to create a new profile on the currently logged in account - * - * @typedef {{name: string, profileImageUrl: string, isRestricted: boolean}} ProfileType - * @param {ProfileType} profile - * @returns - */ - addProfile: async (profile) => { - const authStore = useAuthStore(); - if (!authStore.isLoggedIn) { - throw new Error(); - } + }) + .catch(() => { + throw new Error("Profile not found or not accessible") + }) + }, + + /** + * Upload profile image + * + * @param {Blob} image - the image file contents to upload. Must be a JPEG no bigger than 512kB + * @param {Number} profileId - the ID of the profile to upload this image to + * @returns {Promise<String>} A Promise that resolves to the URL of the uploaded image + */ + uploadProfileImage: async (image, profileId) => { + const authStore = useAuthStore(); - return axios.post(import.meta.env.VITE_BACKEND_URL + '/profile', { - headers: { Authorization: "Bearer " + authStore.token }, - body: profile + let fd = new FormData(); + fd.append("file", image); + fd.append("profileId", profileId); + + return axios.post(`${import.meta.env.VITE_BACKEND_URL}/img`, fd, { + headers: { + Authorization: `Bearer ${authStore.token}`, + } + }) + .then((response) => { + return response.data; + }) + .catch(() => { + throw new Error(); }) + }, + + /** + * Sends a request to create a new profile on the currently logged in account + * + * @typedef {{name: string, id?: number, accountId?: number, profileImageUrl: string, isRestricted: boolean}} ProfileType + * @param {ProfileType} profile - the partial data of profile to create + * @returns {Promise<ProfileType>} the full profile after saving, with id and account ID set + */ + addProfile: async (profile) => { + const authStore = useAuthStore(); + if (!authStore.isLoggedIn) { + throw new Error(); + } + + return axios.post(import.meta.env.VITE_BACKEND_URL + '/profile', profile, { + headers: { Authorization: "Bearer " + authStore.token }, + }) .then((response) => { return response.data; }).catch(() => { throw new Error(); }) - }, + }, - // Returns all profiles to the logged in user - getProfiles: async () => { - const authStore = useAuthStore(); - if (!authStore.isLoggedIn) { - throw new Error(); - } - - return axios.get(import.meta.env.VITE_BACKEND_URL + '/profile', { - headers: { Authorization: "Bearer " + authStore.token }, - }, - ) - .then(response => { - return response.data - }).catch(() => { - throw new Error(); - }); + // Returns all profiles to the logged in user + getProfiles: async () => { + const authStore = useAuthStore(); + if (!authStore.isLoggedIn) { + throw new Error(); + } + + return axios.get(import.meta.env.VITE_BACKEND_URL + '/profile', { + headers: { Authorization: "Bearer " + authStore.token }, }, + ) + .then(response => { + return response.data + }).catch(() => { + throw new Error(); + }); + }, - // Registers a new account and logs into it - addAccount: async (request) => { - const authStore = useAuthStore(); + // Registers a new account and logs into it + addAccount: async (request) => { + const authStore = useAuthStore(); - axios.post(import.meta.env.VITE_BACKEND_URL + '/account', request) - .then(() => { - API.login({email: request.email, password: request.password}) - .catch(err => {console.log(err)}) - }) - .catch(() => {throw new Error()}) - } + axios.post(import.meta.env.VITE_BACKEND_URL + '/account', request) + .then(() => { + API.login({ email: request.email, password: request.password }) + .catch(err => { console.log(err) }) + }) + .catch(() => { throw new Error() }) + } } \ No newline at end of file diff --git a/src/views/ProfileCreationView.vue b/src/views/ProfileCreationView.vue index 22cb341b164554dd4416834a341b895504de2bde..1f0d2c8e3c0076dd8e98c873b0f3c60aa1ea04f0 100644 --- a/src/views/ProfileCreationView.vue +++ b/src/views/ProfileCreationView.vue @@ -1,6 +1,8 @@ <script> -import Toggle from '../components/Toggle.vue'; +import { mapStores } from 'pinia'; +import router from '../router' import { API } from '../util/API'; +import { useAuthStore } from '../stores/authStore' export default { data: () => { @@ -14,20 +16,33 @@ export default { }; }, methods: { - submit() { - this.profile.isRestricted = this.$refs.toggle.state; - API.addProfile(this.profile); + async submit() { + await API.addProfile(this.profile) + .then((profile) => { + let id = profile.id; + + let image = document.getElementById("profile_img").files[0]; + + API.uploadProfileImage(image, id) + .then((updatedProfile) => { + this.authStore.profile = updatedProfile; + router.push("/"); + }); + + }) }, updateImg() { - let file = document.getElementById("avatar").files[0]; + let file = document.getElementById("profile_img").files[0]; let reader = new FileReader(); reader.onload = function (ev) { - document.getElementById("avatar_preview").src = ev.target.result; + document.getElementById("profile_img_preview").src = ev.target.result; }; reader.readAsDataURL(file); } }, - components: { Toggle } + computed: { + ...mapStores(useAuthStore) + } } </script> @@ -36,20 +51,21 @@ export default { <h1>Ny profil</h1> <form action="todo" method="post"> - <label for="avatar" id="avatar_label"> + <label for="profile_img" id="profile_img_label"> <div class="img_hover"> - <img :src="image" alt="fjes" id="avatar_preview"> + <img :src="image" alt="fjes" id="profile_img_preview"> <p class="hover_text"> Klikk for å endre </p> </div> </label> - <input type="file" name="avatar" id="avatar" @change="updateImg"> + <input @change="updateImg" type="file" + accept=".jpeg, .jpg" id="profile_img" name="profile_img"> <label for="name">Navn</label> <input name="name" type="text" v-model="profile.name"> <div class="check_container"> - <label for="child">Begrenset:</label> - <Toggle ref="toggle" /> + <label for="limited">Begrenset:</label> + <input type="checkbox" name="limited" id="limited"> </div> </form> <button @click="submit">Opprett</button> @@ -84,7 +100,7 @@ main { } } -#avatar_label { +#profile_img_label { display: flex; justify-content: center; width: 100%;