]> git.ipfire.org Git - pbs.git/commitdiff
frontend: Create a basic Login view
authorMichael Tremer <michael.tremer@ipfire.org>
Fri, 20 Jun 2025 09:29:51 +0000 (09:29 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Fri, 20 Jun 2025 09:29:51 +0000 (09:29 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
frontend/src/App.vue
frontend/src/components/Container.vue [new file with mode: 0644]
frontend/src/router/index.ts
frontend/src/views/LoginView.vue [new file with mode: 0644]

index 929ecaa6b296694a56f608f3f3336cb46ca614c4..fbcdcd133b9e1fd85d1978e467f749c1b8acd73a 100644 (file)
                                <div class="navbar-start">
 
                                </div>
+
+                               <div class="navbar-end">
+                                       <RouterLink to="/login" class="navbar-item">
+                                               {{ $t("Login") }}
+                                       </RouterLink>
+                               </div>
                        </div>
                </div>
        </nav>
diff --git a/frontend/src/components/Container.vue b/frontend/src/components/Container.vue
new file mode 100644 (file)
index 0000000..ef165ce
--- /dev/null
@@ -0,0 +1,5 @@
+<template>
+       <div class="container">
+               <slot />
+       </div>
+</template>
index 8cd51a1b0ee0b8750eaa5af98ee3a1616c017b31..26f4e0beffdffb87e50c894c7ae77f14c0096acc 100644 (file)
@@ -1,5 +1,7 @@
 import { createRouter, createWebHistory } from 'vue-router'
+
 import HomeView from '../views/HomeView.vue'
+import LoginView from "../views/LoginView.vue"
 
 const router = createRouter({
        history: createWebHistory(import.meta.env.BASE_URL),
@@ -9,6 +11,13 @@ const router = createRouter({
                        name: 'home',
                        component: HomeView,
                },
+
+               // Authentication
+               {
+                       path: "/login",
+                       name: "login",
+                       component: LoginView,
+               },
        ],
 })
 
diff --git a/frontend/src/views/LoginView.vue b/frontend/src/views/LoginView.vue
new file mode 100644 (file)
index 0000000..69395ff
--- /dev/null
@@ -0,0 +1,99 @@
+<script setup lang="ts">
+       import { ref } from "vue"
+       import { useI18n } from "vue-i18n"
+
+       const { t } = useI18n()
+
+       import Notification from "../components/Notification.vue"
+
+       // Error string shown to the user in case something went wrong
+       const error = ref<string | null>(null)
+
+       // Credentials
+       const username = ref<string>("")
+       const password = ref<string>("")
+
+       async function login() {
+               // Reset the error
+               error.value = null
+
+               try {
+                       const response = await fetch("/api/v1/auth/user", {
+                               method : "POST",
+
+                               // Headers
+                               headers : {
+                                       "Content-Type" : "application/x-www-form-urlencoded",
+                               },
+
+                               // Body
+                               body : new URLSearchParams({
+                                       username: username.value,
+                                       password: password.value,
+                               }),
+                       })
+
+                       if (!response.ok) {
+                               throw new Error(t('Invalid username or password'))
+                       }
+
+                       const data = await response.json() as { access_token: string }
+
+                       localStorage.setItem('token', data.access_token)
+                       alert(t('Login successful!'))
+                       // TODO: router.push('/dashboard') or similar
+
+               // Catch any errors
+               } catch (err: unknown) {
+                       error.value = err instanceof Error ? err.message : String(err)
+               }
+       }
+</script>
+
+<template>
+       <Section>
+               <Container>
+                       <div class="columns is-centered">
+                               <div class="column is-one-third">
+                                       <h1 class="title is-1">{{ t("Sign In") }}</h1>
+
+                                       <Notification v-if="error">
+                                               {{ error }}
+                                       </Notification>
+
+                                       <form @submit.prevent="login">
+                                               <div class="field">
+                                                       <p class="control has-icons-left">
+                                                               <input class="input" type="text" v-model="username"
+                                                                               :placeholder="t('Username')" required>
+
+                                                               <span class="icon is-small is-left">
+                                                                       <i class="fas fa-user"></i>
+                                                               </span>
+                                                       </p>
+                                               </div>
+
+                                               <div class="field">
+                                                       <p class="control has-icons-left">
+                                                               <input class="input" type="password" v-model="password"
+                                                                               :placeholder="t('Password')" required autocomplete="off">
+
+                                                               <span class="icon is-small is-left">
+                                                                       <i class="fas fa-lock"></i>
+                                                               </span>
+                                                       </p>
+                                               </div>
+
+                                               <div class="field">
+                                                       <p class="control">
+                                                               <button class="button is-primary is-fullwidth">
+                                                                       {{ t("Sign In") }}
+                                                               </button>
+                                                       </p>
+                                               </div>
+                                       </form>
+                               </div>
+                       </div>
+               </Container>
+       </Section>
+</template>