summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorSanto Cariotti <santo@dcariotti.me>2022-11-12 17:14:40 +0100
committerSanto Cariotti <santo@dcariotti.me>2022-11-12 17:14:40 +0100
commit5e89ad0836ca38b132d1748603be7d8593b9bcb2 (patch)
tree25ec2a2f62b95616b5587754f1bded6e34f890ec /app
parente4e6653f754b6d2857bcf03a89d26c3e24083271 (diff)
Add signup page
Diffstat (limited to 'app')
-rw-r--r--app/.gitignore1
-rw-r--r--app/package-lock.json22
-rw-r--r--app/package.json3
-rw-r--r--app/src/main.ts13
-rw-r--r--app/src/router/index.ts6
-rw-r--r--app/src/sass/main.sass14
-rw-r--r--app/src/store/index.ts27
-rw-r--r--app/src/store/modules/auth.ts80
-rw-r--r--app/src/store/state.ts6
-rw-r--r--app/src/views/Sign.vue92
10 files changed, 257 insertions, 7 deletions
diff --git a/app/.gitignore b/app/.gitignore
index 2fc2afd..600b6a3 100644
--- a/app/.gitignore
+++ b/app/.gitignore
@@ -30,3 +30,4 @@ npm-debug.log*
/platforms
/plugins
/www
+.env.local
diff --git a/app/package-lock.json b/app/package-lock.json
index b16461b..fbf6951 100644
--- a/app/package-lock.json
+++ b/app/package-lock.json
@@ -21,7 +21,8 @@
"sass": "^1.56.1",
"sass-loader": "^13.2.0",
"vue": "^3.2.21",
- "vue-router": "^4.0.12"
+ "vue-router": "^4.0.12",
+ "vuex": "^4.1.0"
},
"devDependencies": {
"@capacitor/cli": "4.4.0",
@@ -17446,6 +17447,17 @@
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
"dev": true
},
+ "node_modules/vuex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/vuex/-/vuex-4.1.0.tgz",
+ "integrity": "sha512-hmV6UerDrPcgbSy9ORAtNXDr9M4wlNP4pEFKye4ujJF8oqgFFuxDCdOLS3eNoRTtq5O3hoBDh9Doj1bQMYHRbQ==",
+ "dependencies": {
+ "@vue/devtools-api": "^6.0.0-beta.11"
+ },
+ "peerDependencies": {
+ "vue": "^3.2.0"
+ }
+ },
"node_modules/w3c-hr-time": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
@@ -31364,6 +31376,14 @@
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
"dev": true
},
+ "vuex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/vuex/-/vuex-4.1.0.tgz",
+ "integrity": "sha512-hmV6UerDrPcgbSy9ORAtNXDr9M4wlNP4pEFKye4ujJF8oqgFFuxDCdOLS3eNoRTtq5O3hoBDh9Doj1bQMYHRbQ==",
+ "requires": {
+ "@vue/devtools-api": "^6.0.0-beta.11"
+ }
+ },
"w3c-hr-time": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
diff --git a/app/package.json b/app/package.json
index 8027c5e..0f0fdaf 100644
--- a/app/package.json
+++ b/app/package.json
@@ -23,7 +23,8 @@
"sass": "^1.56.1",
"sass-loader": "^13.2.0",
"vue": "^3.2.21",
- "vue-router": "^4.0.12"
+ "vue-router": "^4.0.12",
+ "vuex": "^4.1.0"
},
"devDependencies": {
"@capacitor/cli": "4.4.0",
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>