diff --git a/public/profile/default_avatar.png b/public/profile/default_avatar.png new file mode 100644 index 0000000000000000000000000000000000000000..45afcfe8037e158b9ab75b9e7219692e50084770 Binary files /dev/null and b/public/profile/default_avatar.png differ diff --git a/src/components/Navbar.vue b/src/components/Navbar.vue index b9818287498d55d49d7edff45d776343e08745d4..ce884e05ff8b7b890b9032fe7daa7605b81b47af 100644 --- a/src/components/Navbar.vue +++ b/src/components/Navbar.vue @@ -13,10 +13,8 @@ </RouterLink> </li> <li> - <RouterLink :to="'/'" :aria-label="'link to week menu page'"> - <div id = "logoContainer" :style="{ width: logoSize, height: logoSize }" > - <img src="../components/icons/logo.png" alt="logo"> - </div> + <RouterLink :to="'/'" :aria-label="'link to week menu'"> + <Icon icon="ic:baseline-calendar-month" :color="iconColor" :style="{ fontSize: iconSize }"/> </RouterLink> </li> <li> @@ -47,9 +45,6 @@ export default { iconSize() { return `32px`; }, - logoSize() { - return '52px'; - } } } </script> @@ -75,7 +70,9 @@ nav { width: 100%; background-color: base.$green; margin:0; - padding:5px; + qpadding:5px; + align-items: center; + } ul { @@ -84,8 +81,12 @@ ul { justify-content: space-between; align-items: center; padding: 0; + margin-top: .4em; margin-right: 1em; margin-left: 1em; + + + } @media only screen and (min-width: base.$desktop-min){ diff --git a/src/components/Toggle.vue b/src/components/Toggle.vue new file mode 100644 index 0000000000000000000000000000000000000000..adb000f67767329aa2df79ffc90da1a880e468d5 --- /dev/null +++ b/src/components/Toggle.vue @@ -0,0 +1,88 @@ +<script> +export default { + data: () => { + return { + state: false, + } + }, + emits: ['toggled'], + methods: { + emitUpdate() { + this.state = !this.state; + this.$emit('toggled'); + } + } +} +</script> + +<template> + <label class="toggle"> + <input type="checkbox" @change="emitUpdate"> + <span class="toggle_slider"></span> + </label> +</template> + +<style scoped lang="scss"> +$width: 40px; +$height: calc(2 * $width / 3); + +$on-color: green; +$off-color: #cccccc; + +.toggle { + position: relative; + display: inline-block; + + border-radius: calc($height / 2); + + width: $width; + height: $height; + + background-color: $off-color; + + input { + display: none; + } +} + +.toggle_slider { + position: absolute; + cursor: pointer; + + border-radius: calc($height / 2); + + top: 0; + left: 0; + right: 0; + bottom: 0; + + background-color: #ccc; + + &:before { + position: absolute; + content: ""; + + border-radius: 50%; + + height: calc(0.8 * $height); + width: calc(0.8 * $height); + left: calc(0.1 * $height); + top: calc(0.1 * $height); + + background-color: white; + transition: 200ms; + } +} + +input:checked+.toggle_slider { + background-color: $on-color; +} + +input:focus+.toggle_slider { + box-shadow: 0 0 1px white; +} + +input:checked+.toggle_slider:before { + transform: translateX(calc($width / 3)); +} +</style> \ No newline at end of file diff --git a/src/components/__tests__/Navbar.spec.js b/src/components/__tests__/Navbar.spec.js index ba70bb53caef585fbb383a2df2e71bede7258f81..a212573ecee960d8a98e37d63d093b3b2b2a2066 100644 --- a/src/components/__tests__/Navbar.spec.js +++ b/src/components/__tests__/Navbar.spec.js @@ -9,8 +9,8 @@ describe('Navbar', () => { expect(wrapper.findAll('RouterLink').length).toBe(5) }) - it('contains 4 icons', () => { + it('contains 5 icons', () => { const wrapper = mount(Navbar) const icons = wrapper.findAllComponents(Icon) - expect(icons).toHaveLength(4)}) + expect(icons).toHaveLength(5)}) }) diff --git a/src/router/index.js b/src/router/index.js index 3e728b8f1fd8fa9a4c4c68a206a78fefe4b1da96..642a5aecd5c1e5b8a3b2999910473c940ab01d51 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -4,6 +4,7 @@ import MissingPage from "@/views/MissingPage.vue"; import HomeView from '../views/HomeView.vue' import LoginView from '../views/LoginView.vue' import SelectProfileView from '../views/SelectProfileView.vue' +import ProfileCreationView from '../views/ProfileCreationView.vue' import RegisterAccountView from '../views/RegisterAccountView.vue' const router = createRouter({ @@ -24,6 +25,11 @@ const router = createRouter({ name: "selectProfile", component: SelectProfileView }, + { + path: '/newProfile', + name: "newProfile", + component: ProfileCreationView + }, { path: '/registerAccount', name: 'registerAccount', diff --git a/src/util/API.js b/src/util/API.js index a9c0fed48eb1b24ef2ddb52c62a78b85f74edab6..43396b8210de1ed199dcfedc72eb69a9b97d91cc 100644 --- a/src/util/API.js +++ b/src/util/API.js @@ -82,9 +82,28 @@ export const API = { }, - // Sends the user into the "register profile" view - addProfile: async () => { - console.log("todo"); + /** + * 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(); + } + + return axios.post(import.meta.env.VITE_BACKEND_URL + '/profile', { + headers: { Authorization: "Bearer " + authStore.token }, + body: profile + }) + .then((response) => { + return response.data; + }).catch(() => { + throw new Error(); + }) }, // Returns all profiles to the logged in user diff --git a/src/views/ProfileCreationView.vue b/src/views/ProfileCreationView.vue new file mode 100644 index 0000000000000000000000000000000000000000..22cb341b164554dd4416834a341b895504de2bde --- /dev/null +++ b/src/views/ProfileCreationView.vue @@ -0,0 +1,153 @@ +<script> +import Toggle from '../components/Toggle.vue'; +import { API } from '../util/API'; + +export default { + data: () => { + return { + image: "profile/default_avatar.png", + profile: { + profileImageUrl: "", + name: "", + isRestricted: false + } + }; + }, + methods: { + submit() { + this.profile.isRestricted = this.$refs.toggle.state; + API.addProfile(this.profile); + }, + updateImg() { + let file = document.getElementById("avatar").files[0]; + let reader = new FileReader(); + reader.onload = function (ev) { + document.getElementById("avatar_preview").src = ev.target.result; + }; + reader.readAsDataURL(file); + } + }, + components: { Toggle } +} +</script> + +<template> + <main> + <h1>Ny profil</h1> + + <form action="todo" method="post"> + <label for="avatar" id="avatar_label"> + <div class="img_hover"> + <img :src="image" alt="fjes" id="avatar_preview"> + <p class="hover_text"> + Klikk for å endre + </p> + </div> + </label> + <input type="file" name="avatar" id="avatar" @change="updateImg"> + <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" /> + </div> + </form> + <button @click="submit">Opprett</button> + </main> +</template> + +<style scoped lang="scss"> +@import '../style.scss'; + +$gap: 1em; + +$img-size: 150px; + +input[type="file"] { + display: none; +} + +main { + display: flex; + flex-direction: column; + + padding-top: 15%; + + flex-wrap: wrap; + justify-content: center; + align-items: center; + + gap: $gap; + + form { + gap: $gap; + } +} + +#avatar_label { + display: flex; + justify-content: center; + width: 100%; +} + +.check_container { + display: flex; + gap: .5em; +} + +.img_hover { + display: flex; + + justify-content: center; + align-items: center; + + &:hover .hover_text { + visibility: visible; + opacity: 1; + } + + &:hover img { + filter: brightness(70%); + border-radius: 25%; + } + + img { + height: $img-size; + width: $img-size; + border-radius: 50%; + + object-fit: cover; + + transition: all 300ms ease; + } +} + +.hover_text { + position: absolute; + + font-weight: bold; + + color: #fff; + visibility: hidden; + opacity: 0; + + transition: all 350ms eases; +} + +form { + display: flex; + flex-direction: column; + + max-width: 20em; +} + +button { + padding: .5rem 1rem; + border: 2px $green solid; + border-radius: .5rem; + background-color: $light-green; + + color: white; + font-weight: bold; +} +</style> \ No newline at end of file