From 4ad459bdb9af396ffee4c5832ffa799b681d91ab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laczk=C3=B3=20Csongor=20Lor=C3=A1nd?=
 <laczko.csongor.lorand@hallgato.ppke.hu>
Date: Tue, 14 May 2024 22:45:32 +0200
Subject: [PATCH] feat(frontend): Update frontend validation and authorization,
 refactor CSS rules, and fix minor issues

- Overhauled the CSS rules, added the common ones to the index.css and made the media tags more robust.
- Updated the frontend validation to match with backend.
- Made the webpage redirect to login page if authorization is required.
- Minor fixes.
---
 frontend/src/components/auth/Login.vue     |  56 ++-----
 frontend/src/components/auth/Register.vue  |  55 ++-----
 frontend/src/components/common/Footer.vue  |   6 +-
 frontend/src/components/common/Header.vue  |   2 +-
 frontend/src/components/dogs/AddDog.vue    |  26 +--
 frontend/src/components/dogs/AllDogs.vue   |  18 +-
 frontend/src/components/dogs/EditDog.vue   |   2 +-
 frontend/src/components/dogs/SingleDog.vue |  37 ++---
 frontend/src/index.css                     | 181 ++++++++++++---------
 frontend/src/main.js                       |  17 +-
 frontend/src/store.js                      |   8 +
 11 files changed, 171 insertions(+), 237 deletions(-)

diff --git a/frontend/src/components/auth/Login.vue b/frontend/src/components/auth/Login.vue
index 1c4f77e..9615eac 100644
--- a/frontend/src/components/auth/Login.vue
+++ b/frontend/src/components/auth/Login.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="login-container">
+  <div class="global-container">
     <h1 class="title">Bejelentkezés</h1>
     <div v-if="registrationSuccess" class="alert alert-success">
       Regisztráció sikeres! Most már bejelentkezhetsz.
@@ -19,7 +19,7 @@
       <button type="submit">Bejelentkezés</button>
     </form>
     <p>Még nem vagy regisztrálva?</p>
-    <router-link to="/register" tag="button" class="register-button">Regisztrálás</router-link>
+    <router-link to="/register" tag="button" class="button">Regisztrálás</router-link>
   </div>
 </template>
 
@@ -42,7 +42,13 @@ export default {
 
         if (response.status === 200) {
           this.$store.commit('setToken', response.data.token);
-          this.$router.push('/');
+
+          const redirect = this.$route.query.redirect;
+          if (redirect) {
+            this.$router.push(redirect);
+          } else {
+            this.$router.push('/');
+          }
         }
       } catch (error) {
         this.error = 'Helytelen felhasználónév vagy jelszó';
@@ -58,46 +64,4 @@ export default {
     };
   },
 };
-</script>
-
-<style lang="postcss">
-.login-container {
-  @apply flex flex-col items-center justify-center min-h-screen bg-blue-200;
-}
-
-.input-group {
-  @apply mb-4;
-}
-
-label {
-  @apply block mb-2 text-sm font-bold text-gray-700;
-}
-
-input {
-  @apply w-full px-3 py-2 text-sm leading-tight text-gray-700 border rounded shadow appearance-none;
-}
-
-button {
-  @apply px-4 py-2 mt-4 text-sm font-medium text-white bg-blue-500 rounded hover:bg-blue-700 focus:outline-none;
-}
-
-p {
-  @apply mt-10;
-}
-
-.register-button {
-  @apply px-4 py-2 mt-4 text-sm font-medium text-white bg-blue-500 rounded hover:bg-blue-700 focus:outline-none mb-8;
-}
-
-.alert-success {
-  @apply bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative mb-4;
-}
-
-.alert-error {
-  @apply bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-4;
-}
-
-.title {
-  @apply text-4xl font-bold mb-8 text-center p-4;
-}
-</style>
\ No newline at end of file
+</script>
\ No newline at end of file
diff --git a/frontend/src/components/auth/Register.vue b/frontend/src/components/auth/Register.vue
index 1ba6333..f203a74 100644
--- a/frontend/src/components/auth/Register.vue
+++ b/frontend/src/components/auth/Register.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="login-container">
+  <div class="global-container">
     <h1 class="title">Regisztráció</h1>
     <div v-if="errorMessage" class="alert alert-error">
       {{ errorMessage }}
@@ -40,21 +40,16 @@ export default {
     };
 
     const validateForm = () => {
-      const usernameRegex = /^[a-zA-Z0-9]+$/;
-      const minLength = 5;
+      const usernameRegex = /^\w{5,20}$/; // username can contain numbers, upper and lowercase characters
+      const passwordRegex = /^(?=.*[A-Z])(?=.*\d)[A-Za-z\d]{8,20}$/; // password must contain at least one uppercase letter and one digit
 
       if (!username.value) {
         errorMessage.value = 'Felhasználónév megadása kötelező';
         return false;
       }
 
-      if (username.value.length < minLength) {
-        errorMessage.value = 'A felhasználónév legalább 5 karakter hosszú kell legyen';
-        return false;
-      }
-
       if (!username.value.match(usernameRegex)) {
-        errorMessage.value = 'Felhasználónév csak betűket és számokat tartalmazhat';
+        errorMessage.value = 'Érvénytelen felhasználónév. A felhasználónévnek legalább 5 és legfeljebb 20 karakter hosszúnak kell lennie.';
         return false;
       }
 
@@ -63,12 +58,12 @@ export default {
         return false;
       }
 
-      if (password.value.length < minLength) {
-        errorMessage.value = 'A jelszó legalább 5 karakter hosszú kell legyen';
+      if (!password.value.match(passwordRegex)) {
+        errorMessage.value = 'Érvénytelen jelszó. A jelszónak legalább 8 és legfeljebb 20 karakter hosszúnak kell lennie, és tartalmaznia kell legalább egy nagybetűt és egy számot.';
         return false;
       }
 
-      errorMessage.value = '';  // Clear the error message if all fields are valid
+      errorMessage.value = '';
       return true;
     };
 
@@ -90,9 +85,9 @@ export default {
 
         // Display error message
         if (error.response && error.response.data) {
-          errorMessage.value = 'Registration failed: ' + error.response.data;
+          errorMessage.value = 'Regisztráció sikertelen: ' + error.response.data;
         } else {
-          errorMessage.value = 'Registration failed: ' + error.message;
+          errorMessage.value = 'Regisztráció sikertelen: ' + error.message;
         }
       }
     };
@@ -105,34 +100,4 @@ export default {
     };
   },
 };
-</script>
-
-<style lang="postcss">
-.login-container {
-  @apply flex flex-col items-center justify-center min-h-screen bg-blue-200;
-}
-
-.input-group {
-  @apply mb-4;
-}
-
-label {
-  @apply block mb-2 text-sm font-bold text-gray-700;
-}
-
-input {
-  @apply w-full px-3 py-2 text-sm leading-tight text-gray-700 border rounded shadow appearance-none;
-}
-
-button {
-  @apply px-4 py-2 mt-4 text-sm font-medium text-white bg-blue-500 rounded hover:bg-blue-700 focus:outline-none mb-8;
-}
-
-.title {
-  @apply text-4xl font-bold mb-8 text-center p-4;
-}
-
-.alert-error {
-  @apply bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-4;
-}
-</style>
\ No newline at end of file
+</script>
\ No newline at end of file
diff --git a/frontend/src/components/common/Footer.vue b/frontend/src/components/common/Footer.vue
index 73e15f9..8f6c1a9 100644
--- a/frontend/src/components/common/Footer.vue
+++ b/frontend/src/components/common/Footer.vue
@@ -8,8 +8,4 @@
 export default {
   name: 'Footer'
 }
-</script>
-
-<style scoped lang="postcss">
-/* Add your styles here */
-</style>
\ No newline at end of file
+</script>
\ No newline at end of file
diff --git a/frontend/src/components/common/Header.vue b/frontend/src/components/common/Header.vue
index 869c0e9..de22f73 100644
--- a/frontend/src/components/common/Header.vue
+++ b/frontend/src/components/common/Header.vue
@@ -1,6 +1,6 @@
 <template>
   <nav>
-    <h1 class="title">Vau-Vau Alapítvány</h1>
+    <router-link to="/" class="title">Vau-Vau Alapítvány</router-link>
     <ul>
       <li><router-link to="/" class="nav-link" active-class="active-link">Főoldal</router-link></li>
       <li><router-link to="/dogs" class="nav-link" active-class="active-link">Kutyák</router-link></li>
diff --git a/frontend/src/components/dogs/AddDog.vue b/frontend/src/components/dogs/AddDog.vue
index ec553fb..fb0e972 100644
--- a/frontend/src/components/dogs/AddDog.vue
+++ b/frontend/src/components/dogs/AddDog.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="edit-dog-container">
+  <div class="global-container">
     <h1>Kutya hozzáadása</h1>
     <div v-if="errorMessage" class="alert alert-error">
       {{ errorMessage }}
@@ -75,26 +75,4 @@ export default {
     },
   },
 };
-</script>
-
-<style lang="postcss">
-.edit-dog-container {
-  @apply flex flex-col items-center justify-center min-h-screen bg-blue-200;
-}
-
-.input-group {
-  @apply mb-4;
-}
-
-label {
-  @apply block mb-2 text-sm font-bold text-gray-700;
-}
-
-input {
-  @apply w-full px-3 py-2 text-sm leading-tight text-gray-700 border rounded shadow appearance-none;
-}
-
-button {
-  @apply px-4 py-2 mt-4 text-sm font-medium text-white bg-blue-500 rounded hover:bg-blue-700 focus:outline-none;
-}
-</style>
\ No newline at end of file
+</script>
\ No newline at end of file
diff --git a/frontend/src/components/dogs/AllDogs.vue b/frontend/src/components/dogs/AllDogs.vue
index 149f325..a4e258b 100644
--- a/frontend/src/components/dogs/AllDogs.vue
+++ b/frontend/src/components/dogs/AllDogs.vue
@@ -1,6 +1,6 @@
 <template>
-  <div class="dog-list-container">
-    <router-link to="/add-dog" class="add-button">Új kutya hozzáadása</router-link>
+  <div class="global-container">
+    <router-link to="/add-dog" class="button">Új kutya hozzáadása</router-link>
     <div v-if="!loaded">
       Betöltés...
     </div>
@@ -71,31 +71,21 @@ export default {
 </script>
 
 <style scoped lang="postcss">
-.dog-list-container {
-  @apply flex flex-col justify-center items-center flex-grow bg-blue-200;
-}
 
 .dog-item-container {
   @apply flex flex-wrap justify-around p-4 rounded;
 }
 
 .dog-item {
-  @apply flex flex-col items-center bg-white m-4 p-4 rounded shadow w-1/4;
+  @apply flex flex-col items-center bg-white m-4 p-4 rounded shadow w-1/4 min-w-64 min-h-64;
 }
 
 .dog-image {
-  @apply w-full h-64 object-cover mb-4 rounded;
+  @apply w-full h-64 object-cover mb-4 rounded min-w-64 min-h-64;
 }
 
 .dog-name {
   @apply text-lg font-bold;
 }
 
-.alert-error {
-  @apply bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-4;
-}
-
-.add-button {
-  @apply px-4 py-2 mt-4 text-sm font-medium text-white bg-blue-500 rounded hover:bg-blue-700 focus:outline-none mb-8;
-}
 </style>
\ No newline at end of file
diff --git a/frontend/src/components/dogs/EditDog.vue b/frontend/src/components/dogs/EditDog.vue
index 2a907e5..7d6f757 100644
--- a/frontend/src/components/dogs/EditDog.vue
+++ b/frontend/src/components/dogs/EditDog.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="edit-dog-container">
+  <div class="global-container">
     <h1>Kutya szerkesztése</h1>
     <div v-if="errorMessage" class="alert alert-error">
       {{ errorMessage }}
diff --git a/frontend/src/components/dogs/SingleDog.vue b/frontend/src/components/dogs/SingleDog.vue
index a4508b0..002250a 100644
--- a/frontend/src/components/dogs/SingleDog.vue
+++ b/frontend/src/components/dogs/SingleDog.vue
@@ -1,11 +1,11 @@
 <template>
-  <div v-if="dog" class="single-dog flex flex-col items-center bg-gray-100 p-4 rounded shadow">
+  <div v-if="dog" class="global-container">
     <h1 class="text-2xl font-bold mb-4">{{ dog.name }}</h1>
     <img :src="dog.picture" :alt="`${dog.name} képe`" class="w-64 h-64 object-cover mb-4 rounded shadow"/>
     <p class="text-lg mb-2"><strong>Kor:</strong> {{ dog.age }}</p>
     <p class="text-lg"><strong>Faj:</strong> {{ dog.breed }}</p>
-    <router-link :to="`/edit-dog/${dog.id}`" tag="button" class="edit-button">Szerkesztés</router-link>
-    <button @click="deleteDog" class="delete-button">Törlés</button>
+    <router-link :to="`/edit-dog/${dog.id}`" tag="button" class="button">Szerkesztés</router-link>
+    <button @click="deleteDog" class="button delete-button ">Törlés</button>
   </div>
   <div v-else>Betöltés...</div>
 </template>
@@ -36,8 +36,17 @@ export default {
         const config = {
           headers: { Authorization: `Bearer ${this.token}` },
         };
-        await axios.delete(apiURL + `/dogs/${this.$route.params.id}`, config);
-        this.$router.push(`/dogs`);
+        try {
+          await axios.delete(apiURL + `/dogs/${this.$route.params.id}`, config);
+          this.$router.push(`/dogs`);
+        } catch (error) {
+          if (error.response && error.response.status === 401) {
+            this.$router.push('/login');
+          } else {
+            console.error(error);
+            // Handle other types of errors
+          }
+        }
       }
     },
   },
@@ -45,27 +54,11 @@ export default {
 </script>
 
 <style scoped lang="postcss">
-.single-dog {
-  @apply flex flex-col items-center bg-blue-200 p-4 rounded shadow;
-}
-
-h1 {
-  @apply text-2xl font-bold mb-4;
-}
-
 img {
   @apply w-64 h-64 object-cover mb-4 rounded shadow bg-white;
 }
 
-p {
-  @apply text-lg mb-2;
-}
-
-.edit-button {
-  @apply px-4 py-2 mt-4 text-sm font-medium text-white bg-blue-500 rounded hover:bg-blue-700 focus:outline-none;
-}
-
 .delete-button {
-  @apply px-4 py-2 mt-4 text-sm font-medium text-white bg-red-500 rounded hover:bg-red-700 focus:outline-none;
+  @apply bg-red-500 hover:bg-red-700;
 }
 </style>
\ No newline at end of file
diff --git a/frontend/src/index.css b/frontend/src/index.css
index ff55ea7..4a6b44d 100644
--- a/frontend/src/index.css
+++ b/frontend/src/index.css
@@ -50,123 +50,156 @@ p {
     @apply mb-4;
 }
 
-@media (max-width: 600px) {
-    nav ul {
-        @apply flex flex-col space-y-4 items-center;
-    }
-
-    nav ul li {
-        @apply block;
-    }
+.global-container {
+    @apply flex flex-col items-center justify-center min-h-screen bg-blue-200;
+}
 
-    nav ul li a {
-        @apply text-lg;
-    }
+.input-group {
+    @apply mb-4;
+}
 
-    nav h1 {
-        @apply text-2xl;
-    }
+.label {
+    @apply block mb-2 text-sm font-bold text-gray-700;
+}
 
-    .hero {
-        @apply py-12;
-    }
+.input {
+    @apply w-full px-3 py-2 text-sm leading-tight text-gray-700 border rounded shadow appearance-none;
+}
 
-    .features {
-        @apply py-12;
-    }
+.button {
+    @apply px-4 py-2 mt-4 text-sm font-medium text-white bg-blue-500 rounded hover:bg-blue-700 focus:outline-none mb-8;
+}
 
-    .cta {
-        @apply py-12;
-    }
+.title {
+    @apply text-4xl font-bold mb-8 text-center p-4;
+}
 
-    footer {
-        @apply py-4;
-    }
+.alert-error {
+    @apply bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-4;
+}
 
-    footer p {
-        @apply text-sm;
-    }
+.global-container label,
+.global-container input,
+.global-container button {
+    @apply text-lg;
+}
 
-    h2 {
-        @apply text-xl;
-    }
+button {
+    @apply text-lg px-2 py-1 mt-4 mb-4 rounded transition-colors duration-150 ease-in-out;
+}
 
-    p {
-        @apply text-sm;
-    }
+.button {
+    @apply text-lg px-2 py-1 mt-4 mb-4 bg-blue-500 text-white rounded transition-colors duration-150 ease-in-out;
+}
 
-    .login-container {
-        @apply p-4;
-    }
+.button:hover {
+    @apply bg-blue-700;
+}
 
-    .login-container label,
-    .login-container input,
-    .login-container button {
-        @apply text-xs;
-    }
+.alert-success {
+    @apply bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative mb-4;
 }
 
-/* Styles for extra small devices (phones, 600px and below) */
-@media (max-width: 600px) {
-    body {
-        @apply text-sm;
-    }
+.alert-error {
+    @apply bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-4;
+}
 
-    .login-container label,
-    .login-container input,
-    .login-container button {
-        @apply text-sm;
-    }
+.title {
+    @apply text-lg font-bold mb-4;
 }
 
-/* Styles for small devices (portrait tablets and large phones, 600px and up) */
 @media (min-width: 600px) {
-    body {
+        body {
         @apply text-base;
     }
 
-    .login-container label,
-    .login-container input,
-    .login-container button {
+    .global-container {
+        @apply p-6;
+    }
+
+    .global-container label,
+    .global-container input,
+    .global-container button {
         @apply text-base;
     }
+
+        button {
+        @apply text-base px-3 py-2 mt-6 mb-6;
+    }
+
+        .title {
+        @apply text-xl font-bold mb-6;
+    }
 }
 
-/* Styles for medium devices (landscape tablets, 768px and up) */
 @media (min-width: 768px) {
-    body {
+        body {
         @apply text-lg;
     }
 
-    .login-container label,
-    .login-container input,
-    .login-container button {
+    .global-container {
+        @apply p-8;
+    }
+
+    .global-container label,
+    .global-container input,
+    .global-container button {
         @apply text-lg;
     }
+
+        button {
+        @apply text-lg px-4 py-2 mt-8 mb-8;
+    }
+
+        .title {
+        @apply text-2xl font-bold mb-8;
+    }
 }
 
-/* Styles for large devices (laptops/desktops, 992px and up) */
 @media (min-width: 992px) {
-    body {
+        body {
         @apply text-xl;
     }
 
-    .login-container label,
-    .login-container input,
-    .login-container button {
+    .global-container {
+        @apply p-10;
+    }
+
+    .global-container label,
+    .global-container input,
+    .global-container button {
         @apply text-xl;
     }
+
+        button {
+        @apply text-xl px-5 py-3 mt-10 mb-10;
+    }
+
+        .title {
+        @apply text-3xl font-bold mb-10;
+    }
 }
 
-/* Styles for extra large devices (large laptops and desktops, 1200px and up) */
 @media (min-width: 1200px) {
-    body {
+        body {
         @apply text-2xl;
     }
 
-    .login-container label,
-    .login-container input,
-    .login-container button {
+    .global-container {
+        @apply p-12;
+    }
+
+    .global-container label,
+    .global-container input,
+    .global-container button {
         @apply text-2xl;
     }
-}
\ No newline at end of file
+
+        button {
+        @apply text-2xl px-6 py-4 mt-12 mb-12;
+    }
+
+        .title {
+        @apply text-4xl font-bold mb-12;
+    }
+}
diff --git a/frontend/src/main.js b/frontend/src/main.js
index 6259716..d057994 100644
--- a/frontend/src/main.js
+++ b/frontend/src/main.js
@@ -21,7 +21,7 @@ const routes = [
     {path: '/faq', component: FAQ},
     {path: '/dog/:id', name: 'SingleDog', component: SingleDog},
     {path: '/edit-dog/:id', name: 'EditDog', component: EditDog, meta: {requiresAuth: true}},
-    {path: '/login', component: Login},
+    {path: '/login', component: Login, name: 'Login'},
     {path: '/register', component: Register},
     {path: '/add-dog', name: 'AddDog', component: AddDog, meta: {requiresAuth: true}},
     {path: '/:pathMatch(.*)*', redirect: '/'}
@@ -33,11 +33,18 @@ const router = createRouter({
 })
 
 router.beforeEach((to, from, next) => {
-    if (to.name === 'edit' && !store.state.token) {
-        next({ name: 'login' })
+    if (to.meta.requiresAuth) {
+        store.dispatch('checkAuthenticationStatus')
+            .then(isAuthenticated => {
+                if (!isAuthenticated) {
+                    next({ name: 'Login', query: { redirect: to.fullPath } });
+                } else {
+                    next();
+                }
+            });
     } else {
-        next()
+        next();
     }
-})
+});
 
 createApp(App).use(router).use(store).mount('#app');
\ No newline at end of file
diff --git a/frontend/src/store.js b/frontend/src/store.js
index 239cfb9..547800a 100644
--- a/frontend/src/store.js
+++ b/frontend/src/store.js
@@ -23,4 +23,12 @@ export default createStore({
             state.registrationSuccess = success;
         }
     },
+    getters: {
+        isAuthenticated: state => !!state.token,
+    },
+    actions: {
+        checkAuthenticationStatus: ({ getters }) => {
+            return Promise.resolve(getters.isAuthenticated);
+        },
+    },
 });
\ No newline at end of file
-- 
GitLab