Browse Source

better login screen

pull/1666/head
Bernd Storath 6 months ago
parent
commit
4c463af078
  1. 2
      src/app/components/Clients/New.vue
  2. 10
      src/app/components/base/Input.vue
  3. 2
      src/app/components/form/ActionField.vue
  4. 8
      src/app/components/form/DateField.vue
  5. 8
      src/app/components/form/NullTextField.vue
  6. 8
      src/app/components/form/NumberField.vue
  7. 3
      src/app/components/form/PasswordField.vue
  8. 8
      src/app/components/form/TextField.vue
  9. 8
      src/app/pages/clients/[id].vue
  10. 71
      src/app/pages/login.vue
  11. 12
      src/i18n/locales/en.json

2
src/app/components/Clients/New.vue

@ -1,6 +1,6 @@
<template>
<ClientsCreateDialog>
<BaseButton>
<BaseButton as="span">
<IconsPlus class="w-4 md:mr-2" />
<span class="text-sm max-md:hidden">{{ $t('new') }}</span>
</BaseButton>

10
src/app/components/base/Input.vue

@ -0,0 +1,10 @@
<template>
<input
v-model="data"
class="rounded-lg border-2 border-gray-100 text-gray-500 focus:border-red-800 focus:outline-0 focus:ring-0 dark:border-neutral-800 dark:bg-neutral-700 dark:text-neutral-200 dark:placeholder:text-neutral-400"
/>
</template>
<script lang="ts" setup>
const data = defineModel<unknown>();
</script>

2
src/app/components/form/ActionField.vue

@ -2,7 +2,7 @@
<input
:value="label"
:type="type ?? 'button'"
class="col-span-2 rounded-lg border-2 border-gray-100 py-2 text-gray-500 focus:border-red-800 focus:outline-0 focus:ring-0 dark:border-neutral-800 dark:bg-neutral-700 dark:text-neutral-200 dark:placeholder:text-neutral-400"
class="col-span-2 rounded-lg border-2 border-gray-100 py-2 text-gray-500 hover:border-red-800 hover:bg-red-800 hover:text-white focus:border-red-800 focus:outline-0 focus:ring-0 dark:border-neutral-800 dark:bg-neutral-700 dark:text-neutral-200 dark:placeholder:text-neutral-400"
/>
</template>

8
src/app/components/form/DateField.vue

@ -2,13 +2,7 @@
<Label :for="id" class="font-semibold md:align-middle md:leading-10">
{{ label }}
</Label>
<input
:id="id"
v-model="data"
:name="id"
type="date"
class="rounded-lg border-2 border-gray-100 text-gray-500 focus:border-red-800 focus:outline-0 focus:ring-0 dark:border-neutral-800 dark:bg-neutral-700 dark:text-neutral-200 dark:placeholder:text-neutral-400"
/>
<BaseInput :id="id" v-model="data" :name="id" type="date" />
</template>
<script lang="ts" setup>

8
src/app/components/form/NullTextField.vue

@ -7,13 +7,7 @@
<IconsInfo class="size-4" />
</BaseTooltip>
</div>
<input
:id="id"
v-model.trim="data"
:name="id"
type="text"
class="rounded-lg border-2 border-gray-100 text-gray-500 focus:border-red-800 focus:outline-0 focus:ring-0 dark:border-neutral-800 dark:bg-neutral-700 dark:text-neutral-200 dark:placeholder:text-neutral-400"
/>
<BaseInput :id="id" v-model.trim="data" :name="id" type="text" />
</template>
<script lang="ts" setup>

8
src/app/components/form/NumberField.vue

@ -7,13 +7,7 @@
<IconsInfo class="size-4" />
</BaseTooltip>
</div>
<input
:id="id"
v-model.number="data"
:name="id"
type="number"
class="rounded-lg border-2 border-gray-100 text-gray-500 focus:border-red-800 focus:outline-0 focus:ring-0 dark:border-neutral-800 dark:bg-neutral-700 dark:text-neutral-200 dark:placeholder:text-neutral-400"
/>
<BaseInput :id="id" v-model.number="data" :name="id" type="number" />
</template>
<script lang="ts" setup>

3
src/app/components/form/PasswordField.vue

@ -2,13 +2,12 @@
<Label :for="id" class="font-semibold md:align-middle md:leading-10">
{{ label }}
</Label>
<input
<BaseInput
:id="id"
v-model.trim="data"
:name="id"
type="password"
:autocomplete="autocomplete"
class="rounded-lg border-2 border-gray-100 text-gray-500 focus:border-red-800 focus:outline-0 focus:ring-0 dark:border-neutral-800 dark:bg-neutral-700 dark:text-neutral-200 dark:placeholder:text-neutral-400"
/>
</template>

8
src/app/components/form/TextField.vue

@ -7,13 +7,7 @@
<IconsInfo class="size-4" />
</BaseTooltip>
</div>
<input
:id="id"
v-model.trim="data"
:name="id"
type="text"
class="rounded-lg border-2 border-gray-100 text-gray-500 focus:border-red-800 focus:outline-0 focus:ring-0 dark:border-neutral-800 dark:bg-neutral-700 dark:text-neutral-200 dark:placeholder:text-neutral-400"
/>
<BaseInput :id="id" v-model.trim="data" :name="id" type="text" />
</template>
<script lang="ts" setup>

8
src/app/pages/clients/[id].vue

@ -73,7 +73,13 @@
:client-name="data.name"
@delete="deleteClient"
>
<FormActionField label="Delete" class="w-full" />
<FormActionField
label="Delete"
class="w-full"
type="button"
tabindex="-1"
as="span"
/>
</ClientsDeleteDialog>
</FormGroup>
</FormElement>

71
src/app/pages/login.vue

@ -2,83 +2,58 @@
<main>
<UiBanner />
<form
class="mx-auto mt-10 w-64 overflow-hidden rounded-md bg-white p-5 text-gray-700 shadow dark:bg-neutral-700 dark:text-neutral-200"
@submit="login"
class="mx-auto mt-10 flex w-64 flex-col gap-5 overflow-hidden rounded-md bg-white p-5 text-gray-700 shadow dark:bg-neutral-700 dark:text-neutral-200"
@submit.prevent="login"
>
<!-- Avatar -->
<div
class="relative mx-auto mb-10 mt-5 h-20 w-20 overflow-hidden rounded-full bg-red-800 dark:bg-red-800"
class="mx-auto mb-5 mt-5 h-20 w-20 overflow-hidden rounded-full bg-red-800 dark:bg-red-800"
>
<IconsAvatar class="m-5 h-10 w-10 text-white dark:text-white" />
</div>
<input
<BaseInput
v-model="username"
type="text"
name="username"
:placeholder="$t('username')"
:placeholder="$t('general.username')"
autocomplete="username"
autofocus
class="mb-5 w-full rounded-lg border-2 border-gray-100 px-3 py-2 text-sm text-gray-500 focus:border-red-800 focus:outline-0 focus:ring-0 dark:border-neutral-800 dark:bg-neutral-700 dark:text-gray-500 dark:placeholder:text-neutral-400 dark:focus:border-red-800"
name="username"
/>
<input
<BaseInput
v-model="password"
type="password"
name="password"
:placeholder="$t('password')"
:placeholder="$t('general.password')"
autocomplete="current-password"
class="mb-5 w-full rounded-lg border-2 border-gray-100 px-3 py-2 text-sm text-gray-500 focus:border-red-800 focus:outline-0 focus:ring-0 dark:border-neutral-800 dark:bg-neutral-700 dark:text-gray-500 dark:placeholder:text-neutral-400 dark:focus:border-red-800"
/>
<label
class="mb-5 inline-block cursor-pointer whitespace-nowrap"
:title="$t('titleRememberMe')"
class="flex gap-2 whitespace-nowrap"
:title="$t('login.rememberMeDesc')"
>
<input v-model="remember" type="checkbox" class="sr-only" />
<div
v-if="remember"
class="mr-1 inline-block h-6 w-10 cursor-pointer rounded-full bg-red-800 align-middle transition-all hover:bg-red-700"
>
<div class="m-1 ml-5 h-4 w-4 rounded-full bg-white"></div>
</div>
<div
v-if="!remember"
class="mr-1 inline-block h-6 w-10 cursor-pointer rounded-full bg-gray-200 align-middle transition-all hover:bg-gray-300 dark:bg-neutral-400 dark:hover:bg-neutral-500"
>
<div class="m-1 h-4 w-4 rounded-full bg-white"></div>
</div>
<span class="text-sm">{{ $t('rememberMe') }}</span>
<BaseSwitch v-model="remember" />
<span class="text-sm">{{ $t('login.rememberMe') }}</span>
</label>
<button
v-if="authenticating"
class="w-full cursor-not-allowed rounded bg-red-800 py-2 text-sm text-white shadow dark:bg-red-800 dark:text-white"
class="rounded py-2 text-sm text-white shadow transition dark:text-white"
:class="{
'cursor-pointer bg-red-800 hover:bg-red-700 dark:bg-red-800 dark:hover:bg-red-700':
password && username,
'cursor-not-allowed bg-gray-200 dark:bg-neutral-800':
!password || !username,
}"
>
<IconsLoading class="mx-auto w-5 animate-spin" />
<IconsLoading v-if="authenticating" class="mx-auto w-5 animate-spin" />
<span v-else>{{ $t('login.signIn') }}</span>
</button>
<input
v-else
type="submit"
:class="[
{
'cursor-pointer bg-red-800 transition hover:bg-red-700 dark:bg-red-800 dark:hover:bg-red-700':
password,
'cursor-not-allowed bg-gray-200 dark:bg-neutral-800': !password,
},
'w-full rounded py-2 text-sm text-white shadow dark:text-white',
]"
:value="$t('signIn')"
/>
</form>
</main>
</template>
<script setup lang="ts">
// TODO: improve
import { FetchError } from 'ofetch';
const { t } = useI18n();
@ -90,9 +65,7 @@ const password = ref<null | string>(null);
const authStore = useAuthStore();
const toast = useToast();
async function login(e: Event) {
e.preventDefault();
async function login() {
if (!username.value || !password.value || authenticating.value) return;
authenticating.value = true;

12
src/i18n/locales/en.json

@ -19,6 +19,7 @@
},
"general": {
"name": "Name",
"username": "Username",
"password": "Password",
"newPassword": "New Password",
"updatePassword": "Update Password",
@ -51,9 +52,6 @@
"portPlaceholder": "443",
"migration": "Restore the backup"
},
"name": "Name",
"username": "Username",
"signIn": "Sign In",
"logout": "Logout",
"updateAvailable": "There is an update available!",
"update": "Update",
@ -77,12 +75,15 @@
"light": "Light theme",
"system": "System theme"
},
"rememberMe": "Remember me",
"titleRememberMe": "Stay logged after closing the browser",
"sort": "Sort",
"Permanent": "Permanent",
"OneTimeLink": "Generate short one time link",
"errorInit": "Initialization failed.",
"login": {
"signIn": "Sign In",
"rememberMe": "Remember me",
"rememberMeDesc": "Stay logged after closing the browser"
},
"error": {
"clear": "Clear",
"login": "Log in error"
@ -117,7 +118,6 @@
"sectionGeneral": "General",
"sectionAdvanced": "Advanced"
},
"password": "Password",
"admin": {
"general": {
"sessionTimeout": "Session Timeout",

Loading…
Cancel
Save