LoginView.vue 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. <script setup>
  2. import { computed, ref, watch } from "vue";
  3. import { useRoute, useRouter } from "vue-router";
  4. import BotaoAlterarTema from "../../components/BotaoAlterarTema.vue";
  5. import { useAuth } from "../../composables/useAuth.js";
  6. const router = useRouter();
  7. const route = useRoute();
  8. const { login } = useAuth();
  9. const form = ref({
  10. login: "",
  11. senha: "",
  12. rememberMe: false
  13. });
  14. const loading = ref(false);
  15. const error = ref("");
  16. const canSubmit = computed(() => {
  17. return Boolean(form.value.login.trim() && form.value.senha);
  18. });
  19. watch(() => [form.value.login, form.value.senha], () => {
  20. if (error.value) error.value = "";
  21. });
  22. async function onSubmit() {
  23. if (!canSubmit.value || loading.value) return;
  24. loading.value = true;
  25. error.value = "";
  26. try {
  27. await login(form.value);
  28. const raw = typeof route.query.redirect === "string" ? route.query.redirect : "";
  29. const redirect = raw.startsWith("/") && !raw.startsWith("//") ? raw : "/";
  30. await router.replace(redirect);
  31. } catch (err) {
  32. error.value = err?.message || "Falha ao realizar login.";
  33. } finally {
  34. loading.value = false;
  35. }
  36. }
  37. </script>
  38. <template>
  39. <div class="relative min-h-screen overflow-hidden bg-background text-foreground">
  40. <div
  41. aria-hidden="true"
  42. class="pointer-events-none absolute inset-0 z-0 bg-[radial-gradient(900px_520px_at_20%_10%,rgba(37,99,235,0.12),transparent_60%),radial-gradient(700px_520px_at_85%_35%,rgba(162,102,255,0.10),transparent_60%)] dark:hidden"
  43. />
  44. <div
  45. aria-hidden="true"
  46. class="pointer-events-none absolute inset-0 z-0 hidden bg-[radial-gradient(900px_520px_at_20%_10%,rgba(91,140,255,0.18),transparent_60%),radial-gradient(700px_520px_at_85%_35%,rgba(162,102,255,0.14),transparent_60%)] dark:block"
  47. />
  48. <header class="relative z-10 flex items-center justify-between px-6 py-5">
  49. <div>
  50. <div class="text-[16px] font-extrabold tracking-[0.14em]">ORÁCULO</div>
  51. <div class="mt-1 text-sm text-gray-500 dark:text-gray-400">
  52. Acesso ao assistente interno
  53. </div>
  54. </div>
  55. <BotaoAlterarTema />
  56. </header>
  57. <main class="relative z-10 flex min-h-[calc(100vh-88px)] items-center justify-center px-4 pb-8">
  58. <div
  59. class="w-full max-w-md rounded-[28px] border border-gray-300 bg-white/80 p-6 shadow-sm backdrop-blur-md dark:border-white/[0.18] dark:bg-gradient-to-b dark:from-[#101827] dark:to-[#070b16] dark:shadow-[0_18px_50px_rgba(0,0,0,0.35)]"
  60. >
  61. <div class="text-2xl font-extrabold tracking-tight">Entrar</div>
  62. <div class="mt-2 text-sm text-gray-500 dark:text-gray-400">
  63. Informe seu usuario ou email e sua senha para acessar o sistema.
  64. </div>
  65. <form class="mt-6 grid gap-4" @submit.prevent="onSubmit">
  66. <label class="grid gap-1.5">
  67. <span class="text-sm font-medium">Usuario ou email</span>
  68. <input
  69. v-model="form.login"
  70. type="text"
  71. name="username"
  72. autocomplete="username"
  73. placeholder="Digite seu login"
  74. required
  75. aria-required="true"
  76. />
  77. </label>
  78. <label class="grid gap-1.5">
  79. <span class="text-sm font-medium">Senha</span>
  80. <input
  81. v-model="form.senha"
  82. type="password"
  83. name="password"
  84. autocomplete="current-password"
  85. placeholder="Digite sua senha"
  86. required
  87. aria-required="true"
  88. />
  89. </label>
  90. <label class="flex items-center gap-2 text-sm text-gray-600 dark:text-gray-300">
  91. <input
  92. v-model="form.rememberMe"
  93. type="checkbox"
  94. class="!h-4 !w-4 rounded border-gray-300 !p-0"
  95. />
  96. <span>Manter conectado neste navegador</span>
  97. </label>
  98. <div
  99. v-if="error"
  100. class="rounded-xl border border-red-300 bg-red-50 px-3 py-2 text-sm text-red-700 dark:border-red-500/40 dark:bg-red-500/10 dark:text-red-200"
  101. >
  102. {{ error }}
  103. </div>
  104. <button
  105. type="submit"
  106. class="mt-2 inline-flex items-center justify-center rounded-xl border border-primary bg-primary px-4 py-2.5 text-sm font-semibold text-primary-foreground transition-colors hover:border-[var(--primary-600)] hover:bg-[var(--primary-600)] focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed"
  107. :disabled="!canSubmit || loading"
  108. >
  109. {{ loading ? "Entrando..." : "Entrar" }}
  110. </button>
  111. </form>
  112. </div>
  113. </main>
  114. </div>
  115. </template>