diff options
author | Santo Cariotti <santo@dcariotti.me> | 2022-11-12 17:14:40 +0100 |
---|---|---|
committer | Santo Cariotti <santo@dcariotti.me> | 2022-11-12 17:14:40 +0100 |
commit | 5e89ad0836ca38b132d1748603be7d8593b9bcb2 (patch) | |
tree | 25ec2a2f62b95616b5587754f1bded6e34f890ec /app/src | |
parent | e4e6653f754b6d2857bcf03a89d26c3e24083271 (diff) |
Add signup page
Diffstat (limited to 'app/src')
-rw-r--r-- | app/src/main.ts | 13 | ||||
-rw-r--r-- | app/src/router/index.ts | 6 | ||||
-rw-r--r-- | app/src/sass/main.sass | 14 | ||||
-rw-r--r-- | app/src/store/index.ts | 27 | ||||
-rw-r--r-- | app/src/store/modules/auth.ts | 80 | ||||
-rw-r--r-- | app/src/store/state.ts | 6 | ||||
-rw-r--r-- | app/src/views/Sign.vue | 92 |
7 files changed, 233 insertions, 5 deletions
diff --git a/app/src/main.ts b/app/src/main.ts index ce30a3f..59e02a2 100644 --- a/app/src/main.ts +++ b/app/src/main.ts @@ -1,6 +1,7 @@ import { createApp } from 'vue' import App from './App.vue' import router from './router'; +import store from "./store"; import { IonicVue } from '@ionic/vue'; @@ -24,9 +25,11 @@ import '@ionic/vue/css/display.css'; import './theme/variables.css'; const app = createApp(App) - .use(IonicVue) - .use(router); - + .use(IonicVue) + .use(router); + +app.use(store); + router.isReady().then(() => { - app.mount('#app'); -});
\ No newline at end of file + app.mount('#app'); +}); diff --git a/app/src/router/index.ts b/app/src/router/index.ts index 8c87048..a3dd783 100644 --- a/app/src/router/index.ts +++ b/app/src/router/index.ts @@ -1,6 +1,7 @@ import { createRouter, createWebHistory } from "@ionic/vue-router"; import { RouteRecordRaw } from "vue-router"; import HomePage from "../views/HomePage.vue"; +import Sign from "../views/Sign.vue"; const routes: Array<RouteRecordRaw> = [ { path: "/", redirect: "/home" }, @@ -9,6 +10,11 @@ const routes: Array<RouteRecordRaw> = [ component: HomePage, name: "HomePage", }, + { + path: "/sign", + component: Sign, + name: "Sign", + }, ]; const router = createRouter({ diff --git a/app/src/sass/main.sass b/app/src/sass/main.sass index adb721c..3db40de 100644 --- a/app/src/sass/main.sass +++ b/app/src/sass/main.sass @@ -20,6 +20,11 @@ text-decoration: none +ion-tabs + bottom: 0 !important + height: 75px !important + position: relative !important + ion-tab-bar background-color: #4bd786 @@ -31,3 +36,12 @@ ion-tab-button .t width: 30px + +form#login + text-align: center + padding: 50px 10px + + ion-input + border: 1px solid #19703e + padding: 2px 7px !important + margin: 10px diff --git a/app/src/store/index.ts b/app/src/store/index.ts new file mode 100644 index 0000000..6001d90 --- /dev/null +++ b/app/src/store/index.ts @@ -0,0 +1,27 @@ +import { createStore } from "vuex"; +import auth from "./modules/auth"; +import { toastController } from "@ionic/vue"; + +const store = createStore({ + state: { + api: process.env.VUE_APP_BACKEND_URL, + }, + actions: { + // eslint-disable-next-line + async toast({ commit }, data: any) { + const toast = await toastController.create({ + header: data.header, + message: data.text, + color: data.color, + duration: 2000, + position: "top", + }); + return toast.present(); + }, + }, + modules: { + auth, + }, +}); + +export default store; diff --git a/app/src/store/modules/auth.ts b/app/src/store/modules/auth.ts new file mode 100644 index 0000000..b511bff --- /dev/null +++ b/app/src/store/modules/auth.ts @@ -0,0 +1,80 @@ +import { ActionContext } from "vuex"; +import { RootState } from "@/store/state"; + +export interface AuthState { + user: string | null; + token: string | null; +} + +type AuthContext = ActionContext<AuthState, RootState>; + +const auth = { + namespaced: true, + state: { + user: null, + token: localStorage.getItem("access_token") || null, + }, + getters: { + accessToken: (state: AuthState): string | null => { + return state.token; + }, + isLogged: (state: AuthState): boolean => { + return state.token != null; + }, + me: (state: AuthState): any => { + return state.user; + }, + }, + mutations: { + saveAccessToken: (state: AuthState, token: string) => { + localStorage.setItem("access_token", token); + state.token = token; + }, + deleteAccessToken: (state: AuthState) => { + localStorage.removeItem("access_token"); + state.token = null; + localStorage.removeItem("login"); + state.user = null; + }, + saveUserInfo: (state: AuthState, data: any) => { + state.user = data; + }, + }, + actions: { + // Make the login using `credentials`. + // It returns the response in JSON format + async login(context: AuthContext, credentials: any) { + const api = context.rootState.api; + + const res = { status: -1, data: null }; + + console.log(credentials); + + await fetch(`${api}/auth/login`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(credentials), + }) + .then(async (response) => { + const data = await response.json(); + res.data = data; + res.status = response.status; + if (res.status != 200) { + context.commit("deleteAccessToken"); + } else { + context.commit("saveAccessToken", data.access_token); + } + }) + .catch((e) => { + res.status = e.status; + }); + + return res; + }, + logout(context: AuthContext) { + context.commit("deleteAccessToken"); + }, + }, +}; + +export default auth; diff --git a/app/src/store/state.ts b/app/src/store/state.ts new file mode 100644 index 0000000..e4169ff --- /dev/null +++ b/app/src/store/state.ts @@ -0,0 +1,6 @@ +import { AuthState } from "./modules/auth"; + +export interface RootState { + api: string; + auth?: AuthState; +} diff --git a/app/src/views/Sign.vue b/app/src/views/Sign.vue new file mode 100644 index 0000000..54e767a --- /dev/null +++ b/app/src/views/Sign.vue @@ -0,0 +1,92 @@ +<template> + <ion-page> + <m6-header /> + <ion-content :fullscreen="true"> + <form id="login"> + <h2>Sign in</h2> + <ion-item lines="none"> + <ion-input + placeholder="username" + type="text" + :value="form.username" + @ionInput="form.username = $event.target.value" + /> + </ion-item> + <ion-item lines="none"> + <ion-input + placeholder="password" + type="password" + :value="form.password" + @ionInput="form.password = $event.target.value" + /> + </ion-item> + <ion-button color="primary" id="log" @click="handleLogin()"> + Submit + </ion-button> + </form> + </ion-content> + <m6-footer /> + </ion-page> +</template> + +<script lang="ts"> +import { defineComponent } from "vue"; +import { IonPage, IonContent } from "@ionic/vue"; +import Header from "@/components/Header.vue"; +import Footer from "@/components/Footer.vue"; + +import { mapGetters, mapActions } from "vuex"; + +export default defineComponent({ + name: "Sign", + components: { + IonContent, + IonPage, + "m6-header": Header, + "m6-footer": Footer, + }, + data() { + return { + form: { username: "", password: "" }, + }; + }, + computed: { + ...mapGetters("auth", ["isLogged"]), + }, + created() { + if (this.isLogged) window.location.href = "/me"; + }, + methods: { + handleLogin() { + if (this.form.username == "" || this.form.password == "") { + this.toast({ + header: "All fields are required", + text: "", + color: "danger", + }); + return; + } + this.login(this.form).then((response) => { + if (response.status != 200) { + this.toast({ + header: response.data.error, + text: "", + color: "danger", + }); + } else { + this.toast({ + header: "Logged successfully", + text: "", + color: "success", + }); + setTimeout(() => { + window.location.href = "/me"; + }, 2000); + } + }); + }, + ...mapActions(["toast"]), + ...mapActions("auth", ["login"]), + }, +}); +</script> |