From 76e6d5183f9452b9f8061a72d393676c3a23c12d Mon Sep 17 00:00:00 2001 From: L-Nafaryus Date: Fri, 29 Mar 2024 00:13:26 +0500 Subject: [PATCH] frontend: sign up, logout, basic preferencies --- crates/frontend/package-lock.json | 51 +++++++++ crates/frontend/package.json | 1 + .../frontend/src/components/DropdownItem.vue | 0 .../frontend/src/components/DropdownMenu.vue | 22 ++++ crates/frontend/src/components/Error.vue | 5 - .../frontend/src/components/error/Error.vue | 5 + .../frontend/src/directives/click-outside.ts | 14 +++ crates/frontend/src/main.ts | 13 ++- crates/frontend/src/router.ts | 20 +++- crates/frontend/src/services/user.ts | 8 ++ crates/frontend/src/stores/preferencies.ts | 5 + crates/frontend/src/stores/user.ts | 5 + crates/frontend/src/views/Base.vue | 68 +++++++++-- crates/frontend/src/views/SignIn.vue | 10 -- crates/frontend/src/views/User.vue | 54 --------- .../views/{Error.vue => admin/Settings.vue} | 5 +- crates/frontend/src/views/error/NotFound.vue | 10 ++ .../frontend/src/views/user/Preferencies.vue | 38 ++++++ crates/frontend/src/views/user/Profile.vue | 53 +++++++++ .../Login.vue => views/user/SignIn.vue} | 34 ++++-- crates/frontend/src/views/user/SignUp.vue | 61 ++++++++++ .../src/views/user/preferencies/Account.vue | 108 ++++++++++++++++++ .../src/views/user/preferencies/Profile.vue | 67 +++++++++++ src/api/v1/errors.rs | 15 ++- src/api/v1/user.rs | 8 +- 25 files changed, 571 insertions(+), 109 deletions(-) create mode 100644 crates/frontend/src/components/DropdownItem.vue create mode 100644 crates/frontend/src/components/DropdownMenu.vue delete mode 100644 crates/frontend/src/components/Error.vue create mode 100644 crates/frontend/src/components/error/Error.vue create mode 100644 crates/frontend/src/directives/click-outside.ts create mode 100644 crates/frontend/src/stores/preferencies.ts create mode 100644 crates/frontend/src/stores/user.ts delete mode 100644 crates/frontend/src/views/SignIn.vue delete mode 100644 crates/frontend/src/views/User.vue rename crates/frontend/src/views/{Error.vue => admin/Settings.vue} (61%) create mode 100644 crates/frontend/src/views/error/NotFound.vue create mode 100644 crates/frontend/src/views/user/Preferencies.vue create mode 100644 crates/frontend/src/views/user/Profile.vue rename crates/frontend/src/{components/Login.vue => views/user/SignIn.vue} (63%) create mode 100644 crates/frontend/src/views/user/SignUp.vue create mode 100644 crates/frontend/src/views/user/preferencies/Account.vue create mode 100644 crates/frontend/src/views/user/preferencies/Profile.vue diff --git a/crates/frontend/package-lock.json b/crates/frontend/package-lock.json index d2886f0..f372446 100644 --- a/crates/frontend/package-lock.json +++ b/crates/frontend/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "autoprefixer": "^10.4.18", "axios": "^1.6.8", + "pinia": "^2.1.7", "postcss": "^8.4.35", "tailwindcss": "^3.4.1", "vue": "^3.3.11", @@ -2468,6 +2469,56 @@ "node": ">=0.10.0" } }, + "node_modules/pinia": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.1.7.tgz", + "integrity": "sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==", + "dependencies": { + "@vue/devtools-api": "^6.5.0", + "vue-demi": ">=0.14.5" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "@vue/composition-api": "^1.4.0", + "typescript": ">=4.4.4", + "vue": "^2.6.14 || ^3.3.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/pinia/node_modules/vue-demi": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz", + "integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, "node_modules/pirates": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", diff --git a/crates/frontend/package.json b/crates/frontend/package.json index d7f387d..24e1970 100644 --- a/crates/frontend/package.json +++ b/crates/frontend/package.json @@ -13,6 +13,7 @@ "dependencies": { "autoprefixer": "^10.4.18", "axios": "^1.6.8", + "pinia": "^2.1.7", "postcss": "^8.4.35", "tailwindcss": "^3.4.1", "vue": "^3.3.11", diff --git a/crates/frontend/src/components/DropdownItem.vue b/crates/frontend/src/components/DropdownItem.vue new file mode 100644 index 0000000..e69de29 diff --git a/crates/frontend/src/components/DropdownMenu.vue b/crates/frontend/src/components/DropdownMenu.vue new file mode 100644 index 0000000..06dcfac --- /dev/null +++ b/crates/frontend/src/components/DropdownMenu.vue @@ -0,0 +1,22 @@ + + + diff --git a/crates/frontend/src/components/Error.vue b/crates/frontend/src/components/Error.vue deleted file mode 100644 index 730c1af..0000000 --- a/crates/frontend/src/components/Error.vue +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/crates/frontend/src/components/error/Error.vue b/crates/frontend/src/components/error/Error.vue new file mode 100644 index 0000000..f5c1daf --- /dev/null +++ b/crates/frontend/src/components/error/Error.vue @@ -0,0 +1,5 @@ + diff --git a/crates/frontend/src/directives/click-outside.ts b/crates/frontend/src/directives/click-outside.ts new file mode 100644 index 0000000..355621d --- /dev/null +++ b/crates/frontend/src/directives/click-outside.ts @@ -0,0 +1,14 @@ +export const click_outside = { + beforeMount: function(element: any, binding: any) { + element.clickOutsideEvent = function(event: any) { + if (!(element == event.target || element.contains(event.target))) { + binding.value(event); + } + }; + + document.body.addEventListener("click", element.clickOutsideEvent); + }, + unmounted: function(element: any) { + document.body.removeEventListener("click", element.clickOutsideEvent); + } +} diff --git a/crates/frontend/src/main.ts b/crates/frontend/src/main.ts index 46e69d3..6f56389 100644 --- a/crates/frontend/src/main.ts +++ b/crates/frontend/src/main.ts @@ -1,10 +1,15 @@ -import { createApp } from 'vue'; -import App from '@/App.vue'; -import router from '@/router'; +import App from "@/App.vue"; -import '@/assets/style.css'; +import { createApp } from "vue"; +import { createPinia } from "pinia"; + +import router from "@/router"; +import { click_outside } from "@/directives/click-outside"; +import "@/assets/style.css"; createApp(App) + .use(createPinia()) .use(router) + .directive("click-outside", click_outside) .mount('#app'); diff --git a/crates/frontend/src/router.ts b/crates/frontend/src/router.ts index 28b11c5..87534a0 100644 --- a/crates/frontend/src/router.ts +++ b/crates/frontend/src/router.ts @@ -3,10 +3,22 @@ import { createRouter, createWebHistory } from "vue-router"; const router = createRouter({ history: createWebHistory(), routes: [ - { path: "/", component: () => import("@/views/Home.vue") }, - { path: "/user/login", component: () => import("@/views/SignIn.vue") }, - { path: "/:user", name: "User", component: () => import("@/views/User.vue") }, - { path: "/:pathMatch(.*)*", component: () => import("@/views/Error.vue") } + { path: "/", name: "Home", component: () => import("@/views/Home.vue") }, + + { path: "/user/login", name: "SignIn", component: () => import("@/views/user/SignIn.vue") }, + { path: "/user/register", name: "SignUp", component: () => import("@/views/user/SignUp.vue") }, + { + path: "/user/preferencies", name: "Preferencies", redirect: { name: "Preferencies-Profile" }, component: () => import("@/views/user/Preferencies.vue"), children: [ + { path: "profile", name: "Preferencies-Profile", component: () => import("@/views/user/preferencies/Profile.vue") }, + { path: "account", name: "Preferencies-Account", component: () => import("@/views/user/preferencies/Account.vue") }, + ] + }, + + { path: "/:user", name: "Profile", component: () => import("@/views/user/Profile.vue") }, + + { path: "/admin/settings", name: "Settings", component: () => import("@/views/admin/Settings.vue") }, + + { path: "/:pathMatch(.*)*", name: "NotFound", component: () => import("@/views/error/NotFound.vue") } ] }); diff --git a/crates/frontend/src/services/user.ts b/crates/frontend/src/services/user.ts index 03d3351..e76a79e 100644 --- a/crates/frontend/src/services/user.ts +++ b/crates/frontend/src/services/user.ts @@ -1,10 +1,18 @@ import api_client from "@/api-client"; class User { + async register(login: string, email: string, password: string): Promise { + return await api_client.post("/user/register", JSON.stringify({ login: login, email: email, password: password })); + } + async login(email: string, password: string): Promise { return await api_client.post("/user/login", JSON.stringify({ email: email, password: password })); } + async logout(): Promise { + return await api_client.get("/user/logout"); + } + async get(login: any): Promise { return await api_client.get(`/user/${login}`); } diff --git a/crates/frontend/src/stores/preferencies.ts b/crates/frontend/src/stores/preferencies.ts new file mode 100644 index 0000000..bd60a69 --- /dev/null +++ b/crates/frontend/src/stores/preferencies.ts @@ -0,0 +1,5 @@ +import { defineStore } from "pinia"; + +export const usePreferenciesStore = defineStore("preferencies", { + state: () => ({ current_tab: null }), +}); diff --git a/crates/frontend/src/stores/user.ts b/crates/frontend/src/stores/user.ts new file mode 100644 index 0000000..2877855 --- /dev/null +++ b/crates/frontend/src/stores/user.ts @@ -0,0 +1,5 @@ +import { defineStore } from "pinia"; + +export const useUserStore = defineStore("user", { + state: () => ({ login: null }), +}); diff --git a/crates/frontend/src/views/Base.vue b/crates/frontend/src/views/Base.vue index 8967a7f..84224bb 100644 --- a/crates/frontend/src/views/Base.vue +++ b/crates/frontend/src/views/Base.vue @@ -1,10 +1,17 @@ diff --git a/crates/frontend/src/views/SignIn.vue b/crates/frontend/src/views/SignIn.vue deleted file mode 100644 index cce5bcb..0000000 --- a/crates/frontend/src/views/SignIn.vue +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/crates/frontend/src/views/User.vue b/crates/frontend/src/views/User.vue deleted file mode 100644 index 35d8a7b..0000000 --- a/crates/frontend/src/views/User.vue +++ /dev/null @@ -1,54 +0,0 @@ - - - diff --git a/crates/frontend/src/views/Error.vue b/crates/frontend/src/views/admin/Settings.vue similarity index 61% rename from crates/frontend/src/views/Error.vue rename to crates/frontend/src/views/admin/Settings.vue index c6e218b..21c131e 100644 --- a/crates/frontend/src/views/Error.vue +++ b/crates/frontend/src/views/admin/Settings.vue @@ -1,10 +1,11 @@ diff --git a/crates/frontend/src/views/error/NotFound.vue b/crates/frontend/src/views/error/NotFound.vue new file mode 100644 index 0000000..78f9921 --- /dev/null +++ b/crates/frontend/src/views/error/NotFound.vue @@ -0,0 +1,10 @@ + + + diff --git a/crates/frontend/src/views/user/Preferencies.vue b/crates/frontend/src/views/user/Preferencies.vue new file mode 100644 index 0000000..072da86 --- /dev/null +++ b/crates/frontend/src/views/user/Preferencies.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/crates/frontend/src/views/user/Profile.vue b/crates/frontend/src/views/user/Profile.vue new file mode 100644 index 0000000..a305f1d --- /dev/null +++ b/crates/frontend/src/views/user/Profile.vue @@ -0,0 +1,53 @@ + + + diff --git a/crates/frontend/src/components/Login.vue b/crates/frontend/src/views/user/SignIn.vue similarity index 63% rename from crates/frontend/src/components/Login.vue rename to crates/frontend/src/views/user/SignIn.vue index f99387a..a45b819 100644 --- a/crates/frontend/src/components/Login.vue +++ b/crates/frontend/src/views/user/SignIn.vue @@ -1,11 +1,18 @@ diff --git a/crates/frontend/src/views/user/SignUp.vue b/crates/frontend/src/views/user/SignUp.vue new file mode 100644 index 0000000..0586683 --- /dev/null +++ b/crates/frontend/src/views/user/SignUp.vue @@ -0,0 +1,61 @@ + + + diff --git a/crates/frontend/src/views/user/preferencies/Account.vue b/crates/frontend/src/views/user/preferencies/Account.vue new file mode 100644 index 0000000..fec525a --- /dev/null +++ b/crates/frontend/src/views/user/preferencies/Account.vue @@ -0,0 +1,108 @@ + + + diff --git a/crates/frontend/src/views/user/preferencies/Profile.vue b/crates/frontend/src/views/user/preferencies/Profile.vue new file mode 100644 index 0000000..9214e37 --- /dev/null +++ b/crates/frontend/src/views/user/preferencies/Profile.vue @@ -0,0 +1,67 @@ + + + diff --git a/src/api/v1/errors.rs b/src/api/v1/errors.rs index bc90e36..dcc1dff 100644 --- a/src/api/v1/errors.rs +++ b/src/api/v1/errors.rs @@ -15,7 +15,7 @@ pub enum AuthError { impl IntoResponse for AuthError { fn into_response(self) -> axum::response::Response { let (status, message) = match self { - Self::InternalError(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()), + Self::InternalError(e) => (StatusCode::INTERNAL_SERVER_ERROR, format!("Internal Error: {}", e.to_string())), Self::InternalE => (StatusCode::INTERNAL_SERVER_ERROR, "Internal E".to_string()), Self::MissingCredentials => { (StatusCode::BAD_REQUEST, "Missing credentials".to_string()) @@ -28,11 +28,14 @@ impl IntoResponse for AuthError { Self::MissingUser => (StatusCode::UNAUTHORIZED, "User not exists".to_string()), }; - Json(json!({ - "status": status.to_string(), - "message": message - })) - .into_response() + ( + status, + Json(json!({ + "status": status.to_string(), + "message": message + })), + ) + .into_response() } } diff --git a/src/api/v1/user.rs b/src/api/v1/user.rs index 567177d..1a1c5f9 100644 --- a/src/api/v1/user.rs +++ b/src/api/v1/user.rs @@ -22,9 +22,7 @@ use super::token::TokenClaims; pub struct RegisterUser { pub login: String, pub password: String, - pub name: String, pub email: String, - pub is_admin: bool, } #[derive(serde::Serialize)] @@ -65,11 +63,11 @@ pub async fn register( ) -> Result> { let user = User::register( &state.database, - body.login, + body.login.to_owned(), body.password, - body.name, + body.login, //body.name, body.email, - body.is_admin, + false, //body.is_admin, ) .await .map_err(AuthError::InternalError)?;