login.vue
1 <template> 2 <main> 3 <div class="branding"> 4 <h1 class="title"> 5 <svg 6 width="124" 7 height="28" 8 viewBox="0 0 124 28" 9 fill="none" 10 xmlns="http://www.w3.org/2000/svg"> 11 <defs> 12 <clipPath id="clip_path_1"> 13 <rect width="124" height="28" /> 14 </clipPath> 15 </defs> 16 <path 17 d="M29.32 -1.90735e-06L1.32 -1.90735e-06L1.32 5.6L18.12 5.6L1.32 28L29.32 28L29.32 22.4L12.5197 22.4L29.32 -1.90735e-06ZM60.44 5.6L60.44 -1.90735e-06L32.44 -1.90735e-06L32.44 5.6L43.64 5.6L43.64 22.4L32.44 22.4L32.44 28L60.44 28L60.44 22.4L49.24 22.4L49.24 5.6L60.44 5.6ZM92.04 5.6L92.04 -1.90735e-06L64.04 -1.90735e-06L64.04 5.6L75.24 5.6L75.24 22.4L64.04 22.4L64.04 28L92.04 28L92.04 22.4L80.84 22.4L80.84 5.6L92.04 5.6ZM94.72 -1.90735e-06L94.72 5.6L105.92 5.6L105.92 28L111.52 28L111.52 5.6L122.72 5.6L122.72 -1.90735e-06L94.72 -1.90735e-06Z" 18 fill="#E6E6E6" 19 clip-path="url(#clip_path_1)" /> 20 </svg> 21 </h1> 22 <p class="description"> 23 Please sign in to your account or 24 <NuxtLink to="/register"><u>Register</u></NuxtLink> 25 </p> 26 </div> 27 <form 28 class="form" 29 @submit.prevent="login" 30 autocomplete="on" 31 data-form-type="login"> 32 <UiInput 33 v-model="email" 34 placeholder="Email" 35 type="text" 36 :icon="LucideMail" /> 37 <UiInput 38 v-model="password" 39 placeholder="Password" 40 type="password" 41 :icon="LucideKeyRound" /> 42 </form> 43 <div class="buttons"> 44 <UiButton text="Login" keyName="enter" @click="login" /> 45 <UiButton text="Login with Github" keyName="g" @click="githubAuth" /> 46 </div> 47 </main> 48 </template> 49 50 <script setup lang="ts"> 51 import { LucideKeyRound, LucideMail } from "lucide-vue-next"; 52 import { Key } from "@waradu/keyboard"; 53 54 const error = ref(""); 55 const email = ref(""); 56 const password = ref(""); 57 const toast = useToast(); 58 const route = useRoute(); 59 60 onMounted(() => { 61 if (route.query.error) { 62 const errorMessages: Record<string, string> = { 63 invalid_state: "Invalid authentication state, please try again", 64 no_code: "No authorization code received", 65 no_email: "No email address found in your GitHub account", 66 github_auth_failed: "GitHub authentication failed", 67 }; 68 69 const message = 70 errorMessages[route.query.error as string] || "Authentication error"; 71 toast.error(message); 72 } 73 74 if (route.query.success) { 75 const successMessages: Record<string, string> = { 76 logout: "Logged out successfully", 77 }; 78 79 const message = successMessages[route.query.success as string] || "Success"; 80 toast.success(message); 81 } 82 }); 83 84 useKeybind( 85 [Key.G], 86 async () => { 87 await githubAuth(); 88 }, 89 { ignoreIfEditable: true } 90 ); 91 92 useKeybind( 93 [Key.Enter], 94 async () => { 95 await login(); 96 }, 97 { prevent: true } 98 ); 99 100 async function login() { 101 error.value = ""; 102 try { 103 await $fetch("/api/auth/login", { 104 method: "POST", 105 body: { 106 email: email.value, 107 password: password.value, 108 }, 109 }); 110 await navigateTo("/"); 111 } catch (e: any) { 112 error.value = e.data?.message || "Login failed"; 113 toast.error(error.value); 114 } 115 } 116 117 async function githubAuth() { 118 window.location.href = "/api/auth/github"; 119 } 120 121 useSeoMeta({ 122 title: "Ziit - Coding Statistics", 123 description: "Track your coding time and productivity with Ziit", 124 ogTitle: "Ziit - Coding Statistics", 125 ogDescription: "Track your coding time and productivity with Ziit", 126 ogImage: "https://ziit.app/logo.webp", 127 ogUrl: "https://ziit.app/login", 128 ogSiteName: "Ziit", 129 twitterTitle: "Ziit - Coding Statistics", 130 twitterDescription: "Track your coding time and productivity with Ziit", 131 twitterImage: "https://ziit.app/logo.webp", 132 twitterCard: "summary", 133 twitterCreator: "@pandadev_", 134 twitterSite: "@pandadev_", 135 author: "PandaDEV", 136 }); 137 138 useHead({ 139 htmlAttrs: { lang: "en" }, 140 link: [ 141 { 142 rel: "canonical", 143 href: "https://ziit.app/login", 144 }, 145 { 146 rel: "icon", 147 type: "image/ico", 148 href: "/favicon.ico", 149 }, 150 ], 151 script: [ 152 { 153 type: "application/ld+json", 154 innerHTML: JSON.stringify({ 155 "@context": "https://schema.org", 156 "@type": "WebPage", 157 name: "Login - Ziit", 158 url: "https://ziit.app/login", 159 }), 160 }, 161 ], 162 }); 163 </script> 164 165 <style lang="scss"> 166 @use "~~/styles/login.scss"; 167 </style>