diff options
Diffstat (limited to 'src/browser')
| -rw-r--r-- | src/browser/package-lock.json | 19 | ||||
| -rw-r--r-- | src/browser/package.json | 5 | ||||
| -rw-r--r-- | src/browser/src/api/account.js | 76 | ||||
| -rw-r--r-- | src/browser/src/constants.js | 8 | ||||
| -rw-r--r-- | src/browser/src/router.js | 4 | ||||
| -rw-r--r-- | src/browser/src/store.js | 11 | ||||
| -rw-r--r-- | src/browser/src/styles/codyframe/_base.scss | 44 | ||||
| -rw-r--r-- | src/browser/src/styles/codyframe/_btn-states.scss | 51 | ||||
| -rw-r--r-- | src/browser/src/styles/codyframe/_custom-style.scss | 14 | ||||
| -rw-r--r-- | src/browser/src/styles/codyframe/_radios-checkboxes.scss | 149 | ||||
| -rw-r--r-- | src/browser/src/views/Forgot.vue | 66 | ||||
| -rw-r--r-- | src/browser/src/views/Home.vue | 10 | ||||
| -rw-r--r-- | src/browser/src/views/Login.vue | 192 | ||||
| -rw-r--r-- | src/browser/src/views/OidcCallback.vue | 11 |
14 files changed, 327 insertions, 333 deletions
diff --git a/src/browser/package-lock.json b/src/browser/package-lock.json index 2e5511f..ecb17cb 100644 --- a/src/browser/package-lock.json +++ b/src/browser/package-lock.json @@ -1123,6 +1123,11 @@ "to-regex-range": "^5.0.1" } }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==" + }, "follow-redirects": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz", @@ -1752,6 +1757,11 @@ "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", "dev": true }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, "log-symbols": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", @@ -2919,6 +2929,15 @@ "resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.0-beta.4.tgz", "integrity": "sha512-/+4E1dokq5cwbl4mohOqOj8h0vOLOWmLSqlqTf++bfmN9/JKWtwYfsBrzlK0sYrNfuYcpQeX0BVxQHoHXDfYZQ==" }, + "vuex-persist": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vuex-persist/-/vuex-persist-2.2.0.tgz", + "integrity": "sha512-o/qbBeMcKZZqMvCXc7kfIew/5cjHxlP1f53rx5YYp3r2tk2kxXYK/UZumxKn7OXywlurl2r0mgkuBzH6nIWFjw==", + "requires": { + "flatted": "^2.0.0", + "lodash.merge": "^4.6.2" + } + }, "wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", diff --git a/src/browser/package.json b/src/browser/package.json index baec9c1..16f440f 100644 --- a/src/browser/package.json +++ b/src/browser/package.json @@ -8,9 +8,10 @@ }, "dependencies": { "oidc-client": "^1.10.1", - "vue-router": "^4.0.0-0", "vue": "^3.0.0-rc.1", - "vuex": "^4.0.0-0" + "vue-router": "^4.0.0-0", + "vuex": "^4.0.0-0", + "vuex-persist": "^2.2.0" }, "devDependencies": { "@vue/compiler-sfc": "^3.0.0-rc.1", diff --git a/src/browser/src/api/account.js b/src/browser/src/api/account.js index 665eb3f..88522d6 100644 --- a/src/browser/src/api/account.js +++ b/src/browser/src/api/account.js @@ -18,8 +18,6 @@ const userManager = new Oidc.UserManager({ Oidc.Log.logger = console; Oidc.Log.level = Oidc.Log.INFO; -let currentUser; -let signedIn = false; userManager.events.addUserLoaded(function (user) { store.commit("setProfileData", user); @@ -48,9 +46,8 @@ userManager.events.addSilentRenewError(function () { }); userManager.events.addUserSignedOut(function () { - alert("Logout"); console.log("UserSignedOut:", arguments); - //userManager.removeUser(); + userManager.removeUser(); userManager .signoutRedirect() .then(function (resp) { @@ -70,39 +67,23 @@ export default { signinRedirectCallback() { userManager.signinRedirectCallback().then( - () => { - console.log("Logged in"); - }, - (error) => { - console.error(error); - } + (res) => console.log(res), + (error) => console.error(error) ); }, signOut() { - var self = this; userManager .signoutRedirect() .then(function (resp) { - self.signedIn = false; - console.log("signed out", resp); + return true; }) .catch(function (err) { - console.log(err); + console.error(err); + return false; }); }, - showTokens() { - userManager.getUser().then(function (user) { - if (user) { - console.log("Profile", user.profile); - console.log("Role", user.profile.role); - } else { - self.signIn(); - } - }); - }, - getUser() { let self = this; return new Promise((resolve, reject) => { @@ -117,50 +98,7 @@ export default { } }) .catch(function (err) { - console.log(err); - return reject(err); - }); - }); - }, - - getSignedIn() { - let self = this; - return new Promise((resolve, reject) => { - userManager - .getUser() - .then(function (user) { - if (user == null) { - self.signIn(); - return resolve(false); - } else { - currentUser = user; - signedIn = true; - return resolve(signedIn); - } - }) - .catch(function (err) { - console.log(err); - return reject(err); - }); - }); - }, - - getRole() { - let self = this; - return new Promise((resolve, reject) => { - userManager - .getUser() - .then(function (user) { - if (user == null) { - self.signIn(); - return resolve(false); - } else { - currentUser = user; - return resolve(user.profile.role); - } - }) - .catch(function (err) { - console.log(err); + console.error(err); return reject(err); }); }); diff --git a/src/browser/src/constants.js b/src/browser/src/constants.js index 471cc92..cb78214 100644 --- a/src/browser/src/constants.js +++ b/src/browser/src/constants.js @@ -1,12 +1,4 @@ export default { api_address: "http://localhost:5001", client_address: "http://localhost:3000", - storageKeys: { - cookie_last_seen: "cookie-last-seen", - }, - types: { - set_profile_data: "set-profile-data", - login_async: "login-async", - download_profile_data: "download-profile-data-async", - }, }; diff --git a/src/browser/src/router.js b/src/browser/src/router.js index 0fd5462..53df01f 100644 --- a/src/browser/src/router.js +++ b/src/browser/src/router.js @@ -66,9 +66,9 @@ const router = createRouter({ }); router.beforeEach((to, from, next) => { - console.log("store.state.profile.isAuthenticated: " + store.state.profile.isAuthenticated); + console.log("store.state.isAuthenticated: " + store.state.isAuthenticated); const publicPaths = routes.filter((r) => r.isPublic); - if (publicPaths.every((c) => c.path !== to.path) && !store.state.profile.isAuthenticated) + if (publicPaths.every((c) => c.path !== to.path) && !store.state.isAuthenticated) next("/login"); else next(); }); diff --git a/src/browser/src/store.js b/src/browser/src/store.js index c775ed8..ba69ba5 100644 --- a/src/browser/src/store.js +++ b/src/browser/src/store.js @@ -1,15 +1,26 @@ import { createStore } from "vuex"; +import VuexPersistence from "vuex-persist"; + +const vuexLocal = new VuexPersistence({ + storage: window.localStorage, +}); export default createStore({ strict: true, state: { + isAuthenticated: false, profile: {}, }, mutations: { setProfileData(state, profile) { state.profile = profile; + state.isAuthenticated = profile.profile != undefined; + }, + checkForAuthState(state) { + state.isAuthenticated = state.profile.profile != undefined; }, }, actions: {}, modules: {}, + plugins: [vuexLocal.plugin], }); diff --git a/src/browser/src/styles/codyframe/_base.scss b/src/browser/src/styles/codyframe/_base.scss index 5b4bdf6..1810765 100644 --- a/src/browser/src/styles/codyframe/_base.scss +++ b/src/browser/src/styles/codyframe/_base.scss @@ -1,23 +1,23 @@ -@import 'base/reset'; -@import 'base/breakpoints'; -@import 'base/mixins'; -@import 'base/colors'; -@import 'base/spacing'; -@import 'base/grid-layout'; -@import 'base/shared-styles'; -@import 'base/typography'; -@import 'base/icons'; -@import 'base/buttons'; -@import 'base/forms'; -@import 'base/z-index'; -@import 'base/visibility'; -@import 'base/accessibility'; +@import "base/reset"; +@import "base/breakpoints"; +@import "base/mixins"; +@import "base/colors"; +@import "base/spacing"; +@import "base/grid-layout"; +@import "base/shared-styles"; +@import "base/typography"; +@import "base/icons"; +@import "base/buttons"; +@import "base/forms"; +@import "base/z-index"; +@import "base/visibility"; +@import "base/accessibility"; +@import "base/util"; -@import 'base/util'; -@import 'custom-style/colors'; -@import 'custom-style/spacing'; -@import 'custom-style/shared-styles'; -@import 'custom-style/typography'; -@import 'custom-style/icons'; -@import 'custom-style/buttons'; -@import 'custom-style/forms'; +@import "custom-style/colors"; +@import "custom-style/spacing"; +@import "custom-style/shared-styles"; +@import "custom-style/typography"; +@import "custom-style/icons"; +@import "custom-style/buttons"; +@import "custom-style/forms"; diff --git a/src/browser/src/styles/codyframe/_btn-states.scss b/src/browser/src/styles/codyframe/_btn-states.scss index bc1498f..fcb2105 100644 --- a/src/browser/src/styles/codyframe/_btn-states.scss +++ b/src/browser/src/styles/codyframe/_btn-states.scss @@ -8,44 +8,43 @@ Usage: codyhouse.co/license -------------------------------- */ .btn .btn__content-a { - display: inline-flex; + display: inline-flex; } - .btn .btn__content-b { - display: none; + display: none; } -.btn__content-a, .btn__content-b { - align-items: center; +.btn__content-a, +.btn__content-b { + align-items: center; } .btn--state-b { - .btn__content-a { - display: none; - } + .btn__content-a { + display: none; + } - .btn__content-b { - display: inline-block; // fallback - display: inline-flex; - } + .btn__content-b { + display: inline-block; // fallback + display: inline-flex; + } } /* preserve button width when switching from state A to state B */ .btn--preserve-width { - .btn__content-b { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - justify-content: center; - } + .btn__content-b { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + justify-content: center; + } - &.btn--state-b .btn__content-a { - display: inline-block; // fallback - display: inline-flex; - visibility: hidden; - } + &.btn--state-b .btn__content-a { + display: inline-block; // fallback + display: inline-flex; + visibility: hidden; + } } - diff --git a/src/browser/src/styles/codyframe/_custom-style.scss b/src/browser/src/styles/codyframe/_custom-style.scss index 67e3481..3882275 100644 --- a/src/browser/src/styles/codyframe/_custom-style.scss +++ b/src/browser/src/styles/codyframe/_custom-style.scss @@ -1,7 +1,7 @@ -@import 'custom-style/colors'; -@import 'custom-style/spacing'; -@import 'custom-style/shared-styles'; -@import 'custom-style/typography'; -@import 'custom-style/icons'; -@import 'custom-style/buttons'; -@import 'custom-style/forms'; +@import "custom-style/colors"; +@import "custom-style/spacing"; +@import "custom-style/shared-styles"; +@import "custom-style/typography"; +@import "custom-style/icons"; +@import "custom-style/buttons"; +@import "custom-style/forms"; diff --git a/src/browser/src/styles/codyframe/_radios-checkboxes.scss b/src/browser/src/styles/codyframe/_radios-checkboxes.scss new file mode 100644 index 0000000..ec16b8e --- /dev/null +++ b/src/browser/src/styles/codyframe/_radios-checkboxes.scss @@ -0,0 +1,149 @@ + + +/* -------------------------------- + +File#: _1_radios-checkboxes +Title: Radios and Checkboxes +Descr: Custom radio and checkbox buttons +Usage: codyhouse.co/license + +-------------------------------- */ + +:root { + // radios and checkboxes + --checkbox-radio-size: 1em; + --checkbox-radio-translate-y: 0.15em; // edit to align buttons with labels + --checkbox-radio-gap: var(--space-xxxs); // gap between button and label + --checkbox-radio-border-width: 2px; + --checkbox-radio-line-height: var(--body-line-height); + + // radio buttons + --radio-marker-size: 8px; + + // checkboxes + --checkbox-marker-size: 12px; + --checkbox-radius: 0.185em; +} + +.radio, +.checkbox { + // hide native buttons + position: absolute; + margin: 0 !important; + padding: 0 !important; + opacity: 0; + height: 0; + width: 0; + pointer-events: none; +} + +.radio + label, +.checkbox + label { // label style + display: inline-block; // fallback + display: inline-flex; + align-items: baseline; + line-height: var(--checkbox-radio-line-height); + user-select: none; + cursor: pointer; +} + +.radio + label::before, +.checkbox + label::before { // custom buttons - basic style + content: ''; + display: inline-block; + vertical-align: middle; // fallback + flex-shrink: 0; + width: var(--checkbox-radio-size); + height: var(--checkbox-radio-size); + background-color: var(--color-bg); + border-width: var(--checkbox-radio-border-width); + border-color: var(--color-contrast-low); + border-style: solid; + margin-right: var(--checkbox-radio-gap); + background-repeat: no-repeat; + background-position: center; + transition: transform .2s, border .2s; +} + +.radio:not(:checked):not(:focus) + label:hover::before, +.checkbox:not(:checked):not(:focus) + label:hover::before { // :hover + border-color: var(--color-contrast-medium); +} + +@supports (grid-area: auto) { + .radio + label::before, + .checkbox + label::before { + position: relative; + top: var(--checkbox-radio-translate-y); + } +} + +.radio + label::before { + border-radius: 50%; // radio button radius +} + +.checkbox + label::before { + border-radius: var(--checkbox-radius); // checkbox button radius +} + +.radio:checked + label::before, +.checkbox:checked + label::before { + // checked state + background-color: var(--color-primary); + box-shadow: none; + border-color: var(--color-primary); + transition: transform .2s; +} + +.radio:active + label::before, +.checkbox:active + label::before { + // active state + transform: scale(0.8); + transition: transform .2s; +} + +.radio:checked:active + label::before, +.checkbox:checked:active + label::before { + transform: none; + transition: none; +} + +.radio:checked + label::before { + // radio button icon + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cg class='nc-icon-wrapper' fill='%23ffffff'%3E%3Ccircle cx='8' cy='8' r='8' fill='%23ffffff'%3E%3C/circle%3E%3C/g%3E%3C/svg%3E"); + background-size: var(--radio-marker-size); +} + +.checkbox:checked + label::before { + // checkbox button icon + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cg class='nc-icon-wrapper' stroke-width='2' fill='%23ffffff' stroke='%23ffffff'%3E%3Cpolyline fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10' points='1,9 5,13 15,3 ' data-cap='butt'%3E%3C/polyline%3E%3C/g%3E%3C/svg%3E"); + background-size: var(--checkbox-marker-size); +} + +.radio:checked:active + label::before, +.checkbox:checked:active + label::before, +.radio:focus + label::before, +.checkbox:focus + label::before { + // focus state + border-color: var(--color-primary); + box-shadow: 0 0 0 3px alpha(var(--color-primary), 0.2); +} + +// --radio--bg, --checkbox--bg +.radio--bg + label, .checkbox--bg + label { + padding: var(--space-xxxxs) var(--space-xxxs); + border-radius: var(--radius-md); + transition: background .2s; +} + +.radio--bg + label:hover, .checkbox--bg + label:hover { + background-color: var(--color-contrast-lower); +} + +.radio--bg:active + label, +.checkbox--bg:active + label, +.radio--bg:focus + label, +.checkbox--bg:focus + label { + background-color: alpha(var(--color-primary), 0.1); +} + diff --git a/src/browser/src/views/Forgot.vue b/src/browser/src/views/Forgot.vue index 0e78904..efe367c 100644 --- a/src/browser/src/views/Forgot.vue +++ b/src/browser/src/views/Forgot.vue @@ -1,18 +1,14 @@ <template> <div class="container max-width-xs padding-y-lg"> - <form @submit.prevent="submitForm()"> + <form @submit.prevent="submitForm()" class="password-reset-form"> <div class="text-component text-center margin-bottom-sm"> <h1>dough - reset password</h1> </div> <Alert v-if="isError" :title="error.title" :message="error.message" type="error" /> + <div class="margin-bottom-sm"> - <div class="flex justify-between"> - <label class="form-label margin-bottom-xxxs" for="username">Email</label> - <span class="text-sm"> - <a href="#0" v-on:click="login()">Log in?</a> - </span> - </div> + <label class="form-label margin-bottom-xxxs" for="input-email">Email</label> <input class="form-control width-100%" type="text" @@ -26,7 +22,7 @@ <div class="margin-bottom-sm"> <button - class="btn btn--primary btn--md btn--preserve-width width-100%" + class="btn btn--primary btn--md width-100%" :class="{ 'btn--state-b': isLoading, }" @@ -35,13 +31,14 @@ <LoadingIcon class="icon icon--is-spinning" /> </span> <span class="btn__content-a"> - Send resetmail + Request reset link </span> </button> </div> + <div class="text-center"> <p class="text-sm"> - Don't have an account? <a href="#0" v-on:click="signup()">Get started</a> + <a href="http://localhost:5001/login">← Back to login</a> </p> </div> </form> @@ -95,12 +92,51 @@ export default { } } - const login = () => router.replace("/login"); - const signup = () => router.replace("/signup"); - - return { ...toRefs(model), submitForm, signup, login }; + return { ...toRefs(model), submitForm }; }, }; </script> -<style lang="scss"></style> +<style lang="scss"> +.btn .btn__content-a { + display: inline-flex; +} + +.btn .btn__content-b { + display: none; +} + +.btn__content-a, +.btn__content-b { + align-items: center; +} + +.btn--state-b { + .btn__content-a { + display: none; + } + + .btn__content-b { + display: inline-block; // fallback + display: inline-flex; + } +} + +/* preserve button width when switching from state A to state B */ +.btn--preserve-width { + .btn__content-b { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + justify-content: center; + } + + &.btn--state-b .btn__content-a { + display: inline-block; // fallback + display: inline-flex; + visibility: hidden; + } +} +</style> diff --git a/src/browser/src/views/Home.vue b/src/browser/src/views/Home.vue index 0038cbc..528e0ed 100644 --- a/src/browser/src/views/Home.vue +++ b/src/browser/src/views/Home.vue @@ -2,12 +2,14 @@ <div id="home"> <h1>Hjem</h1> <p>Hei {{ username }}, din id er {{ id }}</p> + <button v-on:click="logout()">Logout</button> </div> </template> <script> -import store from "../store"; import { reactive, computed, toRefs } from "vue"; +import store from "../store"; +import account from "../api/account"; export default { setup() { @@ -16,7 +18,11 @@ export default { id: computed(() => store.state.profile.id), }); - return { ...toRefs(model) }; + function logout() { + account.signOut(); + } + + return { ...toRefs(model), logout }; }, }; </script> diff --git a/src/browser/src/views/Login.vue b/src/browser/src/views/Login.vue index a94ea9e..c021fca 100644 --- a/src/browser/src/views/Login.vue +++ b/src/browser/src/views/Login.vue @@ -1,193 +1,29 @@ <template> <div class="container max-width-xs padding-y-lg"> - <form @submit.prevent="submitForm()"> - <div class="text-component text-center margin-bottom-sm"> - <h1>dough</h1> - </div> - <Alert v-if="isError" :title="error.title" :message="error.message" type="error" /> - <div class="margin-bottom-sm"> - <label class="form-label margin-bottom-xxxs" for="username">Email</label> - <input - class="form-control width-100%" - type="text" - name="username" - id="username" - :disabled="isLoading == true" - v-model.trim="input.username" - /> - </div> - - <div class="margin-bottom-sm"> - <div class="flex justify-between margin-bottom-xxxs"> - <label class="form-label" for="password">Password</label> - <span class="text-sm"> - <a href="#0" v-on:click="forgotPassword()">Forgot?</a> - </span> - </div> - - <input - type="password" - class="form-control width-100%" - name="password" - id="password" - :disabled="isLoading == true" - v-model.trim="input.password" - autofocus - /> - </div> - - <div class="margin-bottom-sm"> - <button - class="btn btn--primary btn--md btn--preserve-width width-100%" - :class="{ - 'btn--state-b': isLoading, - }" - > - <span class="btn__content-b"> - <LoadingIcon class="icon icon--is-spinning" /> - </span> - <span class="btn__content-a"> - Login - </span> - </button> - </div> - - <div class="text-center"> - <p class="text-sm"> - Don't have an account? <a href="#0" v-on:click="signup()">Get started</a> - </p> - </div> - </form> + <div class="text-component text-center margin-bottom-sm"> + <h1>dough</h1> + </div> </div> </template> <script> -import Alert from "../components/Alert/Alert.vue"; -import constants from "../constants"; -import { VueComponent as LoadingIcon } from "../assets/icons/loading.svg"; -import { reactive, toRefs } from "vue"; import store from "../store"; import router from "../router"; import account from "../api/account"; - export default { - components: { - Alert, - LoadingIcon, - }, - setup() { - const model = reactive({ - isError: false, - isLoading: false, - error: { - title: "", - message: "", - }, - input: { - username: "", - password: "", - }, - }); - - account.signIn() - .then((user) => console.log(user)) - .catch((err) => console.error(err)); - - function submitForm() {} - /* - async function submitForm() { - model.isLoading = true; - return; - document.getElementById("username").required = true; - document.getElementById("password").required = true; - if (model.input.username && model.input.password) { - model.isLoading = true; - model.isError = false; - try { - let loginResponse = await account.loginAsync( - model.input.username, - model.input.password - ); - if (loginResponse.status === 200) { - let profileResponse = await account.getProfileAsync(); - let profileData = await profileResponse.json(); - if (profileResponse.status === 200) { - model.isLoading = false; - store.commit("setProfileData", profileData); - router.replace("/"); - } else { - displayError(profileData.title, profileData.message); - } - } else { - let errorData = await loginResponse.json(); - displayError(errorData.title, errorData.message); - } - } catch (error) { - console.error(error); - displayError(); - } - } - } -*/ - const forgotPassword = () => router.replace("/forgot"); - const signup = () => router.replace("/signup"); - - function displayError( - title = "An unknown error ocurred", - message = "Please try again soon" - ) { - model.isLoading = false; - model.error.title = title; - model.error.message = message; - model.isError = true; + mounted() { + if (!store.state.profile.profile.sub) { + account + .signIn() + .then((user) => console.log(user)) + .catch((err) => console.error(err)); + } else { + account.getUser().then((res) => { + store.commit("checkForAuthState"); + router.replace("/"); + }); } - - return { ...toRefs(model), ...{ submitForm, signup, forgotPassword } }; }, }; </script> - -<style lang="scss"> -.btn .btn__content-a { - display: inline-flex; -} - -.btn .btn__content-b { - display: none; -} - -.btn__content-a, -.btn__content-b { - align-items: center; -} - -.btn--state-b { - .btn__content-a { - display: none; - } - - .btn__content-b { - display: inline-block; // fallback - display: inline-flex; - } -} - -/* preserve button width when switching from state A to state B */ -.btn--preserve-width { - .btn__content-b { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - justify-content: center; - } - - &.btn--state-b .btn__content-a { - display: inline-block; // fallback - display: inline-flex; - visibility: hidden; - } -} -</style> diff --git a/src/browser/src/views/OidcCallback.vue b/src/browser/src/views/OidcCallback.vue index 10ccd52..1735696 100644 --- a/src/browser/src/views/OidcCallback.vue +++ b/src/browser/src/views/OidcCallback.vue @@ -1,8 +1,15 @@ <template> - <h1>callback</h1> + <p>Loading...</p> </template> <script> import account from "../api/account"; -account.signinRedirectCallback(); +import router from "../router"; +import store from "../store"; + +let url = new URL(location.href).searchParams.get("code"); +if (url) { + account.signinRedirectCallback(); + router.replace("/"); +} </script> |
